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
This commit is contained in:
Josh Hawkins 2024-05-14 10:06:44 -05:00 committed by GitHub
parent b10ae68c1f
commit 1757f4cb04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
80 changed files with 682 additions and 597 deletions

View File

@ -44,6 +44,12 @@ module.exports = {
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
],
"no-console": "error",
"prettier/prettier": [
"warn",
{
plugins: ["prettier-plugin-tailwindcss"],
},
],
},
overrides: [
{

3
web/.prettierrc Normal file
View File

@ -0,0 +1,3 @@
{
"plugins": ["prettier-plugin-tailwindcss"]
}

91
web/package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -31,7 +31,7 @@ function App() {
{isMobile && <Bottombar />}
<div
id="pageRoot"
className={`absolute top-0 right-0 overflow-hidden ${isMobile ? "left-0 bottom-16" : "left-[52px] bottom-8"}`}
className={`absolute right-0 top-0 overflow-hidden ${isMobile ? "bottom-16 left-0" : "bottom-8 left-[52px]"}`}
>
<Suspense>
<Routes>

View File

@ -55,11 +55,11 @@ export default function Statusbar() {
}, [potentialProblems, addMessage, clearMessages]);
return (
<div className="absolute left-0 bottom-0 right-0 w-full h-8 flex justify-between items-center px-4 bg-background_alt z-10 dark:text-secondary-foreground border-t border-secondary-highlight">
<div className="h-full flex items-center gap-2">
<div className="absolute bottom-0 left-0 right-0 z-10 flex h-8 w-full items-center justify-between border-t border-secondary-highlight bg-background_alt px-4 dark:text-secondary-foreground">
<div className="flex h-full items-center gap-2">
{cpuPercent && (
<Link to="/system#general">
<div className="flex items-center text-sm gap-2 cursor-pointer hover:underline">
<div className="flex cursor-pointer items-center gap-2 text-sm hover:underline">
<MdCircle
className={`size-2 ${
cpuPercent < 50
@ -99,7 +99,7 @@ export default function Statusbar() {
{" "}
<div
key={gpuTitle}
className="flex items-center text-sm gap-2 cursor-pointer hover:underline"
className="flex cursor-pointer items-center gap-2 text-sm hover:underline"
>
<MdCircle
className={`size-2 ${
@ -116,20 +116,20 @@ export default function Statusbar() {
);
})}
</div>
<div className="h-full flex items-center gap-2">
<div className="flex h-full items-center gap-2">
{Object.entries(messages).length === 0 ? (
<div className="flex items-center text-sm gap-2">
<div className="flex items-center gap-2 text-sm">
<FaCheck className="size-3 text-green-500" />
System is healthy
</div>
) : (
Object.entries(messages).map(([key, messageArray]) => (
<div key={key} className="h-full flex items-center gap-2">
<div key={key} className="flex h-full items-center gap-2">
{messageArray.map(({ id, text, color, link }: StatusMessage) => {
const message = (
<div
key={id}
className={`flex items-center text-sm gap-2 ${link ? "hover:underline cursor-pointer" : ""}`}
className={`flex items-center gap-2 text-sm ${link ? "cursor-pointer hover:underline" : ""}`}
>
<IoIosWarning
className={`size-5 ${color || "text-danger"}`}

View File

@ -5,7 +5,7 @@ type TWrapperProps = {
};
const Wrapper = ({ children }: TWrapperProps) => {
return <main className="w-screen h-dvh overflow-hidden">{children}</main>;
return <main className="h-dvh w-screen overflow-hidden">{children}</main>;
};
export default Wrapper;

View File

@ -27,11 +27,11 @@ export default function TimelineBar({
return (
<div
className="my-1 p-1 w-full h-18 border rounded cursor-pointer hover:bg-secondary hover:bg-opacity-30"
className="h-18 my-1 w-full cursor-pointer rounded border p-1 hover:bg-secondary hover:bg-opacity-30"
onClick={onClick}
>
{graphData != undefined && (
<div className="relative w-full h-8 flex">
<div className="relative flex h-8 w-full">
{getHourBlocks().map((idx) => {
return (
<div
@ -46,8 +46,8 @@ export default function TimelineBar({
/>
);
})}
<div className="absolute left-0 top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-0 top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:00" : "%I:00%P",
@ -56,8 +56,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[8.3%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[8.3%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:05" : "%I:05%P",
@ -66,8 +66,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[16.7%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[16.7%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:10" : "%I:10%P",
@ -76,8 +76,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[25%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[25%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:15" : "%I:15%P",
@ -86,8 +86,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[33.3%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[33.3%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:20" : "%I:20%P",
@ -96,8 +96,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[41.7%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[41.7%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:25" : "%I:25%P",
@ -106,8 +106,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[50%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[50%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:30" : "%I:30%P",
@ -116,8 +116,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[58.3%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[58.3%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:35" : "%I:35%P",
@ -126,8 +126,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[66.7%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[66.7%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:40" : "%I:40%P",
@ -136,8 +136,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[75%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[75%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:45" : "%I:45%P",
@ -146,8 +146,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[83.3%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[83.3%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:50" : "%I:50%P",
@ -156,8 +156,8 @@ export default function TimelineBar({
})}
</div>
</div>
<div className="absolute left-[91.7%] top-0 bottom-0 align-bottom border-l border-gray-500">
<div className="absolute ml-1 bottom-0 text-sm text-gray-500">
<div className="absolute bottom-0 left-[91.7%] top-0 border-l border-gray-500 align-bottom">
<div className="absolute bottom-0 ml-1 text-sm text-gray-500">
{formatUnixTimestampToDateTime(startTime, {
strftime_fmt:
config?.ui.time_format == "24hour" ? "%H:55" : "%I:55%P",

View File

@ -45,7 +45,7 @@ export default function CameraImage({
{enabled ? (
<img
ref={imgRef}
className={`object-contain ${isPortraitImage ? "h-full w-auto" : "w-full h-auto"} rounded-lg md:rounded-2xl`}
className={`object-contain ${isPortraitImage ? "h-full w-auto" : "h-auto w-full"} rounded-lg md:rounded-2xl`}
onLoad={() => {
setHasLoaded(true);
@ -62,12 +62,12 @@ export default function CameraImage({
}}
/>
) : (
<div className="text-center pt-6">
<div className="pt-6 text-center">
Camera is disabled in config, no stream or snapshot available!
</div>
)}
{!hasLoaded && enabled ? (
<div className="absolute left-0 right-0 top-0 bottom-0 flex justify-center items-center">
<div className="absolute bottom-0 left-0 right-0 top-0 flex items-center justify-center">
<ActivityIndicator />
</div>
) : null}

View File

@ -56,7 +56,7 @@ export default function DebugCameraImage({
cameraClasses="relative w-full h-full flex justify-center"
/>
<Button onClick={handleToggleSettings} variant="link" size="sm">
<span className="w-5 h-5">
<span className="h-5 w-5">
<LuSettings />
</span>{" "}
<span>{showSettings ? "Hide" : "Show"} Options</span>
@ -85,7 +85,7 @@ type DebugSettingsProps = {
function DebugSettings({ handleSetOption, options }: DebugSettingsProps) {
return (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
<div className="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4">
<div className="flex items-center space-x-2">
<Switch
id="bbox"

View File

@ -96,7 +96,7 @@ export default function CameraImage({
return (
<div
className={cn("relative w-full h-full flex justify-center", className)}
className={cn("relative flex h-full w-full justify-center", className)}
ref={containerRef}
>
{enabled ? (
@ -108,7 +108,7 @@ export default function CameraImage({
width={scaledWidth}
/>
) : (
<div className="text-center pt-6">
<div className="pt-6 text-center">
Camera is disabled in config, no stream or snapshot available!
</div>
)}

View File

@ -67,13 +67,13 @@ export function AnimatedEventCard({ event }: AnimatedEventCardProps) {
<Tooltip>
<TooltipTrigger asChild>
<div
className="h-24 4k:h-32 relative"
className="relative h-24 4k:h-32"
style={{
aspectRatio: aspectRatio,
}}
>
<div
className="size-full rounded md:rounded-lg cursor-pointer overflow-hidden"
className="size-full cursor-pointer overflow-hidden rounded md:rounded-lg"
onClick={onOpenReview}
>
{previews ? (
@ -102,8 +102,8 @@ export function AnimatedEventCard({ event }: AnimatedEventCardProps) {
/>
)}
</div>
<div className="absolute bottom-0 inset-x-0 h-6 bg-gradient-to-t from-slate-900/50 to-transparent rounded">
<div className="w-full absolute left-1 bottom-0 text-xs text-white">
<div className="absolute inset-x-0 bottom-0 h-6 rounded bg-gradient-to-t from-slate-900/50 to-transparent">
<div className="absolute bottom-0 left-1 w-full text-xs text-white">
<TimeAgo time={event.start_time * 1000} dense />
</div>
</div>

View File

@ -106,7 +106,7 @@ export default function ExportCard({
<div
className={cn(
"relative aspect-video bg-black rounded-lg md:rounded-2xl flex justify-center items-center",
"relative flex aspect-video items-center justify-center rounded-lg bg-black md:rounded-2xl",
className,
)}
onMouseEnter={
@ -127,19 +127,19 @@ export default function ExportCard({
>
{hovered && (
<>
<div className="absolute inset-0 z-10 bg-black bg-opacity-60 rounded-lg md:rounded-2xl" />
<div className="absolute top-1 right-1 flex items-center gap-2">
<div className="absolute inset-0 z-10 rounded-lg bg-black bg-opacity-60 md:rounded-2xl" />
<div className="absolute right-1 top-1 flex items-center gap-2">
<a
className="z-20"
download
href={`${baseUrl}${exportedRecording.video_path.replace("/media/frigate/", "")}`}
>
<Chip className="bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500 rounded-md cursor-pointer">
<Chip className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500">
<FaDownload className="size-4 text-white" />
</Chip>
</a>
<Chip
className="bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500 rounded-md cursor-pointer"
className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500"
onClick={() =>
setEditName({ original: exportedRecording.name, update: "" })
}
@ -147,7 +147,7 @@ export default function ExportCard({
<MdEditSquare className="size-4 text-white" />
</Chip>
<Chip
className="bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500 rounded-md cursor-pointer"
className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500"
onClick={() =>
onDelete({
file: exportedRecording.id,
@ -155,12 +155,12 @@ export default function ExportCard({
})
}
>
<LuTrash className="size-4 text-destructive fill-destructive" />
<LuTrash className="size-4 fill-destructive text-destructive" />
</Chip>
</div>
<Button
className="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 w-20 h-20 z-20 text-white hover:text-white hover:bg-transparent cursor-pointer"
className="absolute left-1/2 top-1/2 z-20 h-20 w-20 -translate-x-1/2 -translate-y-1/2 cursor-pointer text-white hover:bg-transparent hover:text-white"
variant="ghost"
onClick={() => {
onSelect(exportedRecording);
@ -176,20 +176,20 @@ export default function ExportCard({
<>
{exportedRecording.thumb_path.length > 0 ? (
<img
className="size-full absolute inset-0 object-contain aspect-video rounded-lg md:rounded-2xl"
className="absolute inset-0 aspect-video size-full rounded-lg object-contain md:rounded-2xl"
src={exportedRecording.thumb_path.replace("/media/frigate", "")}
onLoad={() => setLoading(false)}
/>
) : (
<div className="absolute inset-0 bg-secondary rounded-lg md:rounded-2xl" />
<div className="absolute inset-0 rounded-lg bg-secondary md:rounded-2xl" />
)}
</>
)}
{loading && (
<Skeleton className="absolute inset-0 aspect-video rounded-lg md:rounded-2xl" />
)}
<div className="absolute bottom-0 inset-x-0 rounded-b-l z-10 h-[20%] bg-gradient-to-t from-black/60 to-transparent pointer-events-none rounded-lg md:rounded-2xl">
<div className="flex h-full justify-between items-end mx-3 pb-1 text-white text-sm capitalize">
<div className="rounded-b-l pointer-events-none absolute inset-x-0 bottom-0 z-10 h-[20%] rounded-lg bg-gradient-to-t from-black/60 to-transparent md:rounded-2xl">
<div className="mx-3 flex h-full items-end justify-between pb-1 text-sm capitalize text-white">
{exportedRecording.name.replaceAll("_", " ")}
</div>
</div>

View File

@ -35,7 +35,7 @@ export default function ReviewCard({
return (
<div
className="w-full relative flex flex-col gap-1.5 cursor-pointer"
className="relative flex w-full cursor-pointer flex-col gap-1.5"
onClick={onClick}
>
<ImageLoadingIndicator
@ -51,8 +51,8 @@ export default function ReviewCard({
onImgLoad();
}}
/>
<div className="flex justify-between items-center">
<div className="flex justify-evenly items-center gap-1">
<div className="flex items-center justify-between">
<div className="flex items-center justify-evenly gap-1">
{event.data.objects.map((object) => {
return getIconForLabel(object, "size-3 text-white");
})}

View File

@ -41,7 +41,7 @@ export default function CameraFeatureToggle({
onClick={onClick}
className={cn(
className,
"flex flex-col justify-center items-center",
"flex flex-col items-center justify-center",
variants[variant][isActive ? "active" : "inactive"],
)}
>

View File

@ -27,13 +27,13 @@ export default function NewReviewData({
return (
<div className={className}>
<div className="flex justify-center items-center mr-[65px] md:mr-[115px] pointer-events-auto">
<div className="pointer-events-auto mr-[65px] flex items-center justify-center md:mr-[115px]">
<Button
className={`${
hasUpdate
? "animate-in slide-in-from-top duration-500"
? "duration-500 animate-in slide-in-from-top"
: "invisible"
} text-center mt-5 mx-auto bg-gray-400 text-white`}
} mx-auto mt-5 bg-gray-400 text-center text-white`}
onClick={() => {
pullLatestData();
if (contentRef.current) {
@ -44,7 +44,7 @@ export default function NewReviewData({
}
}}
>
<LuRefreshCcw className="w-4 h-4 mr-2" />
<LuRefreshCcw className="mr-2 h-4 w-4" />
New Items To Review
</Button>
</div>

View File

@ -125,8 +125,8 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
<Button
className={
group == "default"
? "text-selected bg-blue-900 focus:bg-blue-900 bg-opacity-60 focus:bg-opacity-60"
: "text-secondary-foreground bg-secondary focus:text-secondary-foreground focus:bg-secondary"
? "bg-blue-900 bg-opacity-60 text-selected focus:bg-blue-900 focus:bg-opacity-60"
: "bg-secondary text-secondary-foreground focus:bg-secondary focus:text-secondary-foreground"
}
size="xs"
onClick={() => (group ? setGroup("default", true) : null)}
@ -149,8 +149,8 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
<Button
className={
group == name
? "text-selected bg-blue-900 focus:bg-blue-900 bg-opacity-60 focus:bg-opacity-60"
: "text-secondary-foreground bg-secondary"
? "bg-blue-900 bg-opacity-60 text-selected focus:bg-blue-900 focus:bg-opacity-60"
: "bg-secondary text-secondary-foreground"
}
size="xs"
onClick={() => setGroup(name, group != "default")}
@ -177,7 +177,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
})}
<Button
className="text-muted-foreground bg-secondary"
className="bg-secondary text-muted-foreground"
size="xs"
onClick={() => setAddGroup(true)}
>
@ -308,16 +308,16 @@ function NewGroupDialog({
}}
>
<Content
className={`min-w-0 ${isMobile ? "w-full p-3 rounded-t-2xl max-h-[90%]" : "w-6/12 max-h-dvh overflow-y-hidden"}`}
className={`min-w-0 ${isMobile ? "max-h-[90%] w-full rounded-t-2xl p-3" : "max-h-dvh w-6/12 overflow-y-hidden"}`}
>
<div className="flex flex-col my-4 overflow-y-auto">
<div className="my-4 flex flex-col overflow-y-auto">
{editState === "none" && (
<>
<div className="flex flex-row justify-between items-center py-2">
<div className="flex flex-row items-center justify-between py-2">
<DialogTitle>Camera Groups</DialogTitle>
<Button
variant="secondary"
className="size-6 p-1 rounded-md text-background bg-secondary-foreground"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
onClick={() => {
setEditState("add");
}}
@ -338,7 +338,7 @@ function NewGroupDialog({
{editState != "none" && (
<>
<div className="flex flex-row justify-between items-center mb-3">
<div className="mb-3 flex flex-row items-center justify-between">
<DialogTitle>
{editState == "add" ? "Add" : "Edit"} Camera Group
</DialogTitle>
@ -398,10 +398,10 @@ export function EditGroupDialog({
}}
>
<DialogContent
className={`min-w-0 ${isMobile ? "w-full p-3 rounded-t-2xl max-h-[90%]" : "w-6/12 max-h-dvh overflow-y-hidden"}`}
className={`min-w-0 ${isMobile ? "max-h-[90%] w-full rounded-t-2xl p-3" : "max-h-dvh w-6/12 overflow-y-hidden"}`}
>
<div className="flex flex-col my-4 overflow-y-auto">
<div className="flex flex-row justify-between items-center mb-3">
<div className="my-4 flex flex-col overflow-y-auto">
<div className="mb-3 flex flex-row items-center justify-between">
<DialogTitle>Edit Camera Group</DialogTitle>
</div>
<CameraGroupEdit
@ -440,7 +440,7 @@ export function CameraGroupRow({
<>
<div
key={group[0]}
className="flex md:p-1 rounded-lg flex-row items-center justify-between my-1.5 transition-background duration-100"
className="transition-background my-1.5 flex flex-row items-center justify-between rounded-lg duration-100 md:p-1"
>
<div className={`flex items-center`}>
<p className="cursor-default">{group[0]}</p>
@ -482,7 +482,7 @@ export function CameraGroupRow({
</>
)}
{!isMobile && (
<div className="flex flex-row gap-2 items-center">
<div className="flex flex-row items-center gap-2">
<Tooltip>
<TooltipTrigger asChild>
<IconWrapper
@ -641,7 +641,7 @@ export function CameraGroupEdit({
<FormLabel>Name</FormLabel>
<FormControl>
<Input
className="w-full p-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
placeholder="Enter a name..."
disabled={editingGroup !== undefined}
{...field}
@ -652,8 +652,8 @@ export function CameraGroupEdit({
)}
/>
<Separator className="flex my-2 bg-secondary" />
<div className="max-h-[25dvh] md:max-h-[40dvh] overflow-y-auto">
<Separator className="my-2 flex bg-secondary" />
<div className="max-h-[25dvh] overflow-y-auto md:max-h-[40dvh]">
<FormField
control={form.control}
name="cameras"
@ -686,7 +686,7 @@ export function CameraGroupEdit({
/>
</div>
<Separator className="flex my-2 bg-secondary" />
<Separator className="my-2 flex bg-secondary" />
<FormField
control={form.control}
name="icon"
@ -711,7 +711,7 @@ export function CameraGroupEdit({
)}
/>
<Separator className="flex my-2 bg-secondary" />
<Separator className="my-2 flex bg-secondary" />
<div className="flex flex-row gap-2 py-5 md:pb-0">
<Button type="button" className="flex flex-1" onClick={onCancel}>

View File

@ -14,9 +14,9 @@ export default function FilterSwitch({
onCheckedChange,
}: FilterSwitchProps) {
return (
<div className="flex justify-between items-center gap-1">
<div className="flex items-center justify-between gap-1">
<Label
className={`w-full mx-2 text-primary capitalize cursor-pointer ${disabled ? "text-secondary-foreground" : ""}`}
className={`mx-2 w-full cursor-pointer capitalize text-primary ${disabled ? "text-secondary-foreground" : ""}`}
htmlFor={label}
>
{label}

View File

@ -19,7 +19,7 @@ export function LogLevelFilterButton({
const trigger = (
<Button size="sm" className="flex items-center gap-2">
<FaFilter className="text-secondary-foreground" />
<div className="hidden md:block text-primary">Filter</div>
<div className="hidden text-primary md:block">Filter</div>
</Button>
);
const content = (
@ -33,7 +33,7 @@ export function LogLevelFilterButton({
return (
<Drawer>
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
<DrawerContent className="max-h-[75dvh] p-3 mx-1 overflow-hidden">
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden p-3">
{content}
</DrawerContent>
</Drawer>
@ -59,9 +59,9 @@ export function GeneralFilterContent({
return (
<>
<div className="h-auto overflow-y-auto overflow-x-hidden">
<div className="flex justify-between items-center my-2.5">
<div className="my-2.5 flex items-center justify-between">
<Label
className="mx-2 text-primary cursor-pointer"
className="mx-2 cursor-pointer text-primary"
htmlFor="allLabels"
>
All Logs
@ -80,9 +80,9 @@ export function GeneralFilterContent({
<DropdownMenuSeparator />
<div className="my-2.5 flex flex-col gap-2.5">
{["debug", "info", "warning", "error"].map((item) => (
<div className="flex justify-between items-center">
<div className="flex items-center justify-between">
<Label
className="w-full mx-2 text-primary capitalize cursor-pointer"
className="mx-2 w-full cursor-pointer capitalize text-primary"
htmlFor={item}
>
{item.replaceAll("_", " ")}

View File

@ -35,12 +35,12 @@ export default function ReviewActionGroup({
}, [selectedReviews, setSelectedReviews, pullLatestData]);
return (
<div className="absolute inset-x-2 inset-y-0 md:left-auto py-2 flex gap-2 justify-between items-center bg-background">
<div className="mx-1 flex justify-center items-center text-sm text-muted-foreground">
<div className="absolute inset-x-2 inset-y-0 flex items-center justify-between gap-2 bg-background py-2 md:left-auto">
<div className="mx-1 flex items-center justify-center text-sm text-muted-foreground">
<div className="p-1">{`${selectedReviews.length} selected`}</div>
<div className="p-1">{"|"}</div>
<div
className="p-2 text-primary cursor-pointer hover:bg-secondary hover:rounded-lg"
className="cursor-pointer p-2 text-primary hover:rounded-lg hover:bg-secondary"
onClick={onClearSelected}
>
Unselect
@ -49,7 +49,7 @@ export default function ReviewActionGroup({
<div className="flex items-center gap-1 md:gap-2">
{selectedReviews.length == 1 && (
<Button
className="p-2 flex items-center gap-2"
className="flex items-center gap-2 p-2"
size="sm"
onClick={() => {
onExport(selectedReviews[0]);
@ -61,7 +61,7 @@ export default function ReviewActionGroup({
</Button>
)}
<Button
className="p-2 flex items-center gap-2"
className="flex items-center gap-2 p-2"
size="sm"
onClick={onMarkAsReviewed}
>
@ -69,7 +69,7 @@ export default function ReviewActionGroup({
{isDesktop && <div className="text-primary">Mark as reviewed</div>}
</Button>
<Button
className="p-2 flex items-center gap-2"
className="flex items-center gap-2 p-2"
size="sm"
onClick={onDelete}
>

View File

@ -263,7 +263,7 @@ export function CamerasFilterButton({
<DropdownMenuSeparator />
</>
)}
<div className="h-auto max-h-[80dvh] p-4 overflow-y-auto overflow-x-hidden">
<div className="h-auto max-h-[80dvh] overflow-y-auto overflow-x-hidden p-4">
<FilterSwitch
isChecked={currentCameras == undefined}
label="All Cameras"
@ -280,7 +280,7 @@ export function CamerasFilterButton({
return (
<div
key={name}
className="w-full px-2 py-1.5 text-sm text-primary capitalize cursor-pointer rounded-lg hover:bg-muted"
className="w-full cursor-pointer rounded-lg px-2 py-1.5 text-sm capitalize text-primary hover:bg-muted"
onClick={() => setCurrentCameras([...conf.cameras])}
>
{name}
@ -321,7 +321,7 @@ export function CamerasFilterButton({
</div>
</div>
<DropdownMenuSeparator className="my-2" />
<div className="p-2 flex justify-evenly items-center">
<div className="flex items-center justify-evenly p-2">
<Button
variant="select"
onClick={() => {
@ -394,7 +394,7 @@ function ShowReviewFilter({
);
return (
<>
<div className="hidden h-9 md:flex p-2 justify-start items-center text-sm bg-secondary hover:bg-secondary/80 rounded-md cursor-pointer">
<div className="hidden h-9 cursor-pointer items-center justify-start rounded-md bg-secondary p-2 text-sm hover:bg-secondary/80 md:flex">
<Switch
id="reviewed"
checked={showReviewedSwitch == 1}
@ -408,7 +408,7 @@ function ShowReviewFilter({
</div>
<Button
className="block md:hidden duration-0"
className="block duration-0 md:hidden"
variant={showReviewedSwitch == 1 ? "select" : "default"}
size="sm"
onClick={() => setShowReviewedSwitch(showReviewedSwitch == 0 ? 1 : 0)}
@ -460,7 +460,7 @@ function CalendarFilterButton({
onSelect={updateSelectedDay}
/>
<DropdownMenuSeparator />
<div className="p-2 flex justify-center items-center">
<div className="flex items-center justify-center p-2">
<Button
onClick={() => {
updateSelectedDay(undefined);
@ -621,9 +621,9 @@ export function GeneralFilterContent({
<DropdownMenuSeparator />
</div>
)}
<div className="flex justify-between items-center my-2.5">
<div className="my-2.5 flex items-center justify-between">
<Label
className="mx-2 text-primary cursor-pointer"
className="mx-2 cursor-pointer text-primary"
htmlFor="allLabels"
>
All Labels
@ -666,7 +666,7 @@ export function GeneralFilterContent({
</div>
</div>
<DropdownMenuSeparator />
<div className="p-2 flex justify-evenly items-center">
<div className="flex items-center justify-evenly p-2">
<Button
variant="select"
onClick={() => {
@ -707,7 +707,7 @@ function ShowMotionOnlyButton({
return (
<>
<div className="hidden md:inline-flex items-center justify-center whitespace-nowrap text-sm bg-secondary hover:bg-secondary/80 text-primary h-9 rounded-md px-3 mx-1 cursor-pointer">
<div className="mx-1 hidden h-9 cursor-pointer items-center justify-center whitespace-nowrap rounded-md bg-secondary px-3 text-sm text-primary hover:bg-secondary/80 md:inline-flex">
<Switch
className="ml-1"
id="collapse-motion"
@ -715,7 +715,7 @@ function ShowMotionOnlyButton({
onCheckedChange={setMotionOnlyButton}
/>
<Label
className="mx-2 text-primary cursor-pointer"
className="mx-2 cursor-pointer text-primary"
htmlFor="collapse-motion"
>
Motion only

View File

@ -43,7 +43,7 @@ export function ZoneMaskFilterButton({
return (
<Drawer>
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
<DrawerContent className="max-h-[75dvh] p-3 mx-1 overflow-hidden">
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden p-3">
{content}
</DrawerContent>
</Drawer>
@ -69,9 +69,9 @@ export function GeneralFilterContent({
return (
<>
<div className="h-auto overflow-y-auto overflow-x-hidden">
<div className="flex justify-between items-center my-2.5">
<div className="my-2.5 flex items-center justify-between">
<Label
className="mx-2 text-primary cursor-pointer"
className="mx-2 cursor-pointer text-primary"
htmlFor="allLabels"
>
All Masks and Zones
@ -90,9 +90,9 @@ export function GeneralFilterContent({
<DropdownMenuSeparator />
<div className="my-2.5 flex flex-col gap-2.5">
{["zone", "motion_mask", "object_mask"].map((item) => (
<div key={item} className="flex justify-between items-center">
<div key={item} className="flex items-center justify-between">
<Label
className="w-full mx-2 text-primary capitalize cursor-pointer"
className="mx-2 w-full cursor-pointer capitalize text-primary"
htmlFor={item}
>
{item

View File

@ -131,7 +131,7 @@ export function ThresholdBarGraph({
}, [graphId, options]);
return (
<div className="w-full flex flex-col">
<div className="flex w-full flex-col">
<div className="flex items-center gap-1">
<div className="text-xs text-muted-foreground">{name}</div>
<div className="text-xs text-primary">
@ -234,8 +234,8 @@ export function StorageGraph({ graphId, used, total }: StorageGraphProps) {
}, [graphId, options]);
return (
<div className="w-full flex flex-col gap-2.5">
<div className="w-full flex justify-between items-center gap-1">
<div className="flex w-full flex-col gap-2.5">
<div className="flex w-full items-center justify-between gap-1">
<div className="flex items-center gap-1">
<div className="text-xs text-primary">{getUnitSize(used)}</div>
<div className="text-xs text-primary">/</div>
@ -247,7 +247,7 @@ export function StorageGraph({ graphId, used, total }: StorageGraphProps) {
{Math.round((used / total) * 100)}%
</div>
</div>
<div className="h-5 rounded-md overflow-hidden">
<div className="h-5 overflow-hidden rounded-md">
<Chart
type="bar"
options={options}
@ -369,7 +369,7 @@ export function CameraLineGraph({
}, [graphId, options]);
return (
<div className="w-full flex flex-col">
<div className="flex w-full flex-col">
{lastValues && (
<div className="flex items-center gap-2.5">
{dataLabels.map((label, labelIdx) => (

View File

@ -66,12 +66,12 @@ export default function IconPicker({
>
<PopoverTrigger asChild>
{!selectedIcon?.name || !selectedIcon?.Icon ? (
<Button className="text-muted-foreground w-full mt-2">
<Button className="mt-2 w-full text-muted-foreground">
Select an icon
</Button>
) : (
<div className="hover:cursor-pointer">
<div className="flex flex-row w-full justify-between items-center gap-2 my-3">
<div className="my-3 flex w-full flex-row items-center justify-between gap-2">
<div className="flex flex-row items-center gap-2">
<selectedIcon.Icon size={15} />
<div className="text-sm">
@ -95,9 +95,9 @@ export default function IconPicker({
align="start"
side="top"
container={containerRef.current}
className="flex flex-col max-h-[50dvh] md:max-h-[30dvh] overflow-y-hidden"
className="flex max-h-[50dvh] flex-col overflow-y-hidden md:max-h-[30dvh]"
>
<div className="flex flex-row justify-between items-center mb-3">
<div className="mb-3 flex flex-row items-center justify-between">
<Heading as="h4">Select an icon</Heading>
<span tabIndex={0} className="sr-only" />
<IoClose
@ -111,17 +111,17 @@ export default function IconPicker({
<Input
type="text"
placeholder="Search for an icon..."
className="mb-3 text-md md:text-sm"
className="text-md mb-3 md:text-sm"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<div className="flex flex-col h-full overflow-y-auto">
<div className="flex h-full flex-col overflow-y-auto">
<div className="grid grid-cols-6 gap-2 pr-1">
{icons.map(([name, Icon]) => (
<div
key={name}
className={cn(
"flex flex-row justify-center items-center hover:cursor-pointer p-1 rounded-lg",
"flex flex-row items-center justify-center rounded-lg p-1 hover:cursor-pointer",
selectedIcon?.name === name
? "bg-selected text-white"
: "hover:bg-secondary-foreground",

View File

@ -4,11 +4,11 @@ type LiveIconProps = {
export function LiveGridIcon({ layout }: LiveIconProps) {
return (
<div className="size-full flex flex-col gap-0.5 rounded-md overflow-hidden">
<div className="flex size-full flex-col gap-0.5 overflow-hidden rounded-md">
<div
className={`h-1 w-full ${layout == "grid" ? "bg-selected" : "bg-muted-foreground"}`}
/>
<div className="h-1 w-full flex gap-0.5">
<div className="flex h-1 w-full gap-0.5">
<div
className={`w-full ${layout == "grid" ? "bg-selected" : "bg-muted-foreground"}`}
/>
@ -16,7 +16,7 @@ export function LiveGridIcon({ layout }: LiveIconProps) {
className={`w-full ${layout == "grid" ? "bg-selected" : "bg-muted-foreground"}`}
/>
</div>
<div className="h-1 w-full flex gap-0.5">
<div className="flex h-1 w-full gap-0.5">
<div
className={`w-full ${layout == "grid" ? "bg-selected" : "bg-muted-foreground"}`}
/>
@ -30,7 +30,7 @@ export function LiveGridIcon({ layout }: LiveIconProps) {
export function LiveListIcon({ layout }: LiveIconProps) {
return (
<div className="size-full flex flex-col gap-0.5 rounded-md overflow-hidden">
<div className="flex size-full flex-col gap-0.5 overflow-hidden rounded-md">
<div
className={`size-full ${layout == "list" ? "bg-selected" : "bg-secondary-foreground"}`}
/>

View File

@ -1,12 +1,12 @@
function CameraActivityIndicator() {
return (
<div className="flex items-center justify-center relative z-20">
<div className="relative z-20 flex items-center justify-center">
<div className="flex">
<div className="absolute size-[5px] inset-0 bg-severity_alert-dimmed rounded-full shadow-[0px_0px_10px_0px_#00000024,0px_0px_15px_0px_#00000024] z-20 animate-move"></div>
<div className="flex-1 size-[5px] mr-[2px] bg-severity_alert rounded-full animate-scale1"></div>
<div className="flex-1 size-[5px] mr-[2px] bg-severity_alert rounded-full animate-scale2"></div>
<div className="flex-1 size-[5px] mr-[2px] bg-severity_alert rounded-full animate-scale3"></div>
<div className="flex-1 size-[5px] mr-[2px] bg-severity_alert rounded-full animate-scale4"></div>
<div className="absolute inset-0 z-20 size-[5px] animate-move rounded-full bg-severity_alert-dimmed shadow-[0px_0px_10px_0px_#00000024,0px_0px_15px_0px_#00000024]"></div>
<div className="mr-[2px] size-[5px] flex-1 animate-scale1 rounded-full bg-severity_alert"></div>
<div className="mr-[2px] size-[5px] flex-1 animate-scale2 rounded-full bg-severity_alert"></div>
<div className="mr-[2px] size-[5px] flex-1 animate-scale3 rounded-full bg-severity_alert"></div>
<div className="mr-[2px] size-[5px] flex-1 animate-scale4 rounded-full bg-severity_alert"></div>
</div>
<svg className="hidden" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>

View File

@ -34,7 +34,7 @@ export default function Chip({
<div
ref={nodeRef}
className={cn(
"flex px-2 py-1.5 rounded-2xl items-center z-10",
"z-10 flex items-center rounded-2xl px-2 py-1.5",
className,
)}
onClick={onClick}
@ -63,7 +63,7 @@ export function LogChip({ severity, onClickSeverity }: LogChipProps) {
return (
<div
className={`py-[1px] px-1 capitalize text-xs rounded-md ${onClickSeverity ? "cursor-pointer" : ""} ${severityClassName}`}
className={`rounded-md px-1 py-[1px] text-xs capitalize ${onClickSeverity ? "cursor-pointer" : ""} ${severityClassName}`}
onClick={(e) => {
e.stopPropagation();

View File

@ -14,7 +14,7 @@ export default function ImageLoadingIndicator({
}
return isSafari ? (
<div className={cn("bg-gray-300 pointer-events-none", className)} />
<div className={cn("pointer-events-none bg-gray-300", className)} />
) : (
<Skeleton className={cn("pointer-events-none", className)} />
);

View File

@ -17,9 +17,9 @@ export default function AccountSettings({ className }: AccountSettingsProps) {
<TooltipTrigger asChild>
<div
className={cn(
"flex flex-col justify-center items-center",
"flex flex-col items-center justify-center",
isDesktop
? "rounded-lg text-secondary-foreground bg-secondary hover:bg-muted cursor-pointer"
? "cursor-pointer rounded-lg bg-secondary text-secondary-foreground hover:bg-muted"
: "text-secondary-foreground",
className,
)}

View File

@ -120,7 +120,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
<Tooltip>
<TooltipTrigger asChild>
<div
className={`flex flex-col justify-center items-center ${isDesktop ? "rounded-lg text-secondary-foreground bg-secondary hover:bg-muted cursor-pointer" : "text-secondary-foreground"}`}
className={`flex flex-col items-center justify-center ${isDesktop ? "cursor-pointer rounded-lg bg-secondary text-secondary-foreground hover:bg-muted" : "text-secondary-foreground"}`}
>
<LuSettings className="size-5 md:m-[6px]" />
</div>
@ -135,7 +135,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
</Trigger>
<Content
className={
isDesktop ? "w-72 mr-5" : "max-h-[75dvh] p-2 overflow-hidden"
isDesktop ? "mr-5 w-72" : "max-h-[75dvh] overflow-hidden p-2"
}
>
<div className="w-full flex-col overflow-y-auto overflow-x-hidden">
@ -147,7 +147,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
className={
isDesktop
? "cursor-pointer"
: "w-full p-2 flex items-center text-sm"
: "flex w-full items-center p-2 text-sm"
}
>
<LuActivity className="mr-2 size-4" />
@ -159,7 +159,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
className={
isDesktop
? "cursor-pointer"
: "w-full p-2 flex items-center text-sm"
: "flex w-full items-center p-2 text-sm"
}
>
<LuList className="mr-2 size-4" />
@ -177,7 +177,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
className={
isDesktop
? "cursor-pointer"
: "w-full p-2 flex items-center text-sm"
: "flex w-full items-center p-2 text-sm"
}
>
<LuSettings className="mr-2 size-4" />
@ -189,7 +189,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
className={
isDesktop
? "cursor-pointer"
: "w-full p-2 flex items-center text-sm"
: "flex w-full items-center p-2 text-sm"
}
>
<LuPenSquare className="mr-2 size-4" />
@ -205,7 +205,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
className={
isDesktop
? "cursor-pointer"
: "p-2 flex items-center text-sm"
: "flex items-center p-2 text-sm"
}
>
<LuSunMoon className="mr-2 size-4" />
@ -222,7 +222,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
className={
isDesktop
? "cursor-pointer"
: "p-2 flex items-center text-sm"
: "flex items-center p-2 text-sm"
}
onClick={() => setTheme("light")}
>
@ -232,14 +232,14 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
Light
</>
) : (
<span className="mr-2 ml-6">Light</span>
<span className="ml-6 mr-2">Light</span>
)}
</MenuItem>
<MenuItem
className={
isDesktop
? "cursor-pointer"
: "p-2 flex items-center text-sm"
: "flex items-center p-2 text-sm"
}
onClick={() => setTheme("dark")}
>
@ -249,14 +249,14 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
Dark
</>
) : (
<span className="mr-2 ml-6">Dark</span>
<span className="ml-6 mr-2">Dark</span>
)}
</MenuItem>
<MenuItem
className={
isDesktop
? "cursor-pointer"
: "p-2 flex items-center text-sm"
: "flex items-center p-2 text-sm"
}
onClick={() => setTheme("system")}
>
@ -266,7 +266,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
System
</>
) : (
<span className="mr-2 ml-6">System</span>
<span className="ml-6 mr-2">System</span>
)}
</MenuItem>
</SubItemContent>
@ -277,7 +277,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
className={
isDesktop
? "cursor-pointer"
: "p-2 flex items-center text-sm"
: "flex items-center p-2 text-sm"
}
>
<LuSunMoon className="mr-2 size-4" />
@ -296,7 +296,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
className={
isDesktop
? "cursor-pointer"
: "p-2 flex items-center text-sm"
: "flex items-center p-2 text-sm"
}
onClick={() => setColorScheme(scheme)}
>
@ -306,7 +306,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
{friendlyColorSchemeName(scheme)}
</>
) : (
<span className="mr-2 ml-6">
<span className="ml-6 mr-2">
{friendlyColorSchemeName(scheme)}
</span>
)}
@ -325,7 +325,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
className={
isDesktop
? "cursor-pointer"
: "p-2 flex items-center text-sm"
: "flex items-center p-2 text-sm"
}
>
<LuLifeBuoy className="mr-2 size-4" />
@ -337,7 +337,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
className={
isDesktop
? "cursor-pointer"
: "p-2 flex items-center text-sm"
: "flex items-center p-2 text-sm"
}
>
<LuGithub className="mr-2 size-4" />
@ -347,7 +347,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
<DropdownMenuSeparator className={isDesktop ? "mt-3" : "mt-1"} />
<MenuItem
className={
isDesktop ? "cursor-pointer" : "p-2 flex items-center text-sm"
isDesktop ? "cursor-pointer" : "flex items-center p-2 text-sm"
}
onClick={() => setRestartDialogOpen(true)}
>

View File

@ -20,7 +20,7 @@ function Bottombar() {
const navItems = useNavigation("secondary");
return (
<div className="absolute h-16 inset-x-4 bottom-0 flex flex-row items-center justify-between">
<div className="absolute inset-x-4 bottom-0 flex h-16 flex-row items-center justify-between">
{navItems.map((item) => (
<NavItem key={item.id} className="p-2" item={item} Icon={item.icon} />
))}
@ -77,16 +77,16 @@ function StatusAlertNav({ className }: StatusAlertNavProps) {
</DrawerTrigger>
<DrawerContent
className={cn(
"max-h-[75dvh] px-2 mx-1 rounded-t-2xl overflow-hidden",
"mx-1 max-h-[75dvh] overflow-hidden rounded-t-2xl px-2",
className,
)}
>
<div className="w-full h-auto py-4 overflow-y-auto overflow-x-hidden flex flex-col items-center gap-2">
<div className="flex h-auto w-full flex-col items-center gap-2 overflow-y-auto overflow-x-hidden py-4">
{Object.entries(messages).map(([key, messageArray]) => (
<div key={key} className="w-full flex items-center gap-2">
<div key={key} className="flex w-full items-center gap-2">
{messageArray.map(({ id, text, color, link }: StatusMessage) => {
const message = (
<div key={id} className="flex items-center text-xs gap-2">
<div key={id} className="flex items-center gap-2 text-xs">
<IoIosWarning
className={`size-5 ${color || "text-danger"}`}
/>

View File

@ -44,7 +44,7 @@ export default function NavItem({
onClick={onClick}
className={({ isActive }) =>
cn(
"flex flex-col justify-center items-center rounded-lg",
"flex flex-col items-center justify-center rounded-lg",
className,
variants[item.variant ?? "primary"][isActive ? "active" : "inactive"],
)

View File

@ -12,10 +12,10 @@ function Sidebar() {
const navbarLinks = useNavigation();
return (
<aside className="absolute w-[52px] z-10 left-o inset-y-0 overflow-y-auto scrollbar-hidden py-4 flex flex-col justify-between bg-background_alt border-r border-secondary-highlight">
<aside className="left-o scrollbar-hidden absolute inset-y-0 z-10 flex w-[52px] flex-col justify-between overflow-y-auto border-r border-secondary-highlight bg-background_alt py-4">
<span tabIndex={0} className="sr-only" />
<div className="w-full flex flex-col gap-0 items-center">
<Logo className="w-8 h-8 mb-6" />
<div className="flex w-full flex-col items-center gap-0">
<Logo className="mb-6 h-8 w-8" />
{navbarLinks.map((item) => {
const showCameraGroups =
item.id == 1 && item.url == location.pathname;
@ -32,7 +32,7 @@ function Sidebar() {
);
})}
</div>
<div className="flex flex-col items-center gap-4 mb-8">
<div className="mb-8 flex flex-col items-center gap-4">
<GeneralSettings />
<AccountSettings />
</div>

View File

@ -103,7 +103,7 @@ export default function ExportDialog({
return (
<>
<SaveExportOverlay
className="absolute top-8 left-1/2 -translate-x-1/2 z-50 pointer-events-none"
className="pointer-events-none absolute left-1/2 top-8 z-50 -translate-x-1/2"
show={mode == "timeline"}
onSave={() => onStartExport()}
onCancel={() => setMode("none")}
@ -132,7 +132,7 @@ export default function ExportDialog({
setMode("select");
}}
>
<FaArrowDown className="p-1 fill-secondary bg-secondary-foreground rounded-md" />
<FaArrowDown className="rounded-md bg-secondary-foreground fill-secondary p-1" />
{isDesktop && <div className="text-primary">Export</div>}
</Button>
</Trigger>
@ -140,7 +140,7 @@ export default function ExportDialog({
className={
isDesktop
? "sm:rounded-lg md:rounded-2xl"
: "px-4 pb-4 mx-4 rounded-lg md:rounded-2xl"
: "mx-4 rounded-lg px-4 pb-4 md:rounded-2xl"
}
>
<ExportContent
@ -241,8 +241,8 @@ export function ExportContent({
<RadioGroupItem
className={
opt == selectedOption
? "from-selected/50 to-selected/90 text-selected bg-selected"
: "from-secondary/50 to-secondary/90 text-secondary bg-secondary"
? "bg-selected from-selected/50 to-selected/90 text-selected"
: "bg-secondary from-secondary/50 to-secondary/90 text-secondary"
}
id={opt}
value={opt}
@ -277,7 +277,7 @@ export function ExportContent({
className={isDesktop ? "" : "mt-3 flex flex-col-reverse gap-4"}
>
<div
className={`p-2 cursor-pointer text-center ${isDesktop ? "" : "w-full"}`}
className={`cursor-pointer p-2 text-center ${isDesktop ? "" : "w-full"}`}
onClick={onCancel}
>
Cancel
@ -383,7 +383,7 @@ function CustomTimeSelector({
return (
<div
className={`mt-3 flex items-center bg-secondary text-secondary-foreground rounded-lg ${isDesktop ? "mx-8 px-2 gap-2" : "pl-2"}`}
className={`mt-3 flex items-center rounded-lg bg-secondary text-secondary-foreground ${isDesktop ? "mx-8 gap-2 px-2" : "pl-2"}`}
>
<FaCalendarAlt />
<Popover
@ -424,7 +424,7 @@ function CustomTimeSelector({
/>
<SelectSeparator className="bg-secondary" />
<input
className="w-full mx-4 p-1 border border-input bg-background text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
className="mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
id="startTime"
type="time"
value={startClock}
@ -486,7 +486,7 @@ function CustomTimeSelector({
/>
<SelectSeparator className="bg-secondary" />
<input
className="w-full mx-4 p-1 border border-input bg-background text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
className="mx-4 w-full border border-input bg-background p-1 text-secondary-foreground hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
id="startTime"
type="time"
value={endClock}

View File

@ -29,11 +29,11 @@ export default function LogInfoDialog({
}}
>
<Content
className={isDesktop ? "" : "max-h-[75dvh] p-2 pb-4 overflow-hidden"}
className={isDesktop ? "" : "max-h-[75dvh] overflow-hidden p-2 pb-4"}
>
{logLine && (
<div className="size-full flex flex-col gap-5">
<div className="w-min flex flex-col gap-1.5">
<div className="flex size-full flex-col gap-5">
<div className="flex w-min flex-col gap-1.5">
<div className="text-sm text-primary/40">Type</div>
<LogChip severity={logLine.severity} />
</div>

View File

@ -27,12 +27,12 @@ export default function MobileCameraDrawer({
<FaVideo className="text-secondary-foreground" />
</Button>
</DrawerTrigger>
<DrawerContent className="max-h-[75dvh] px-4 mx-1 rounded-t-2xl overflow-hidden">
<div className="w-full h-auto py-4 overflow-y-auto overflow-x-hidden flex flex-col items-center gap-2">
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden rounded-t-2xl px-4">
<div className="flex h-auto w-full flex-col items-center gap-2 overflow-y-auto overflow-x-hidden py-4">
{allCameras.map((cam) => (
<div
key={cam}
className={`w-full mx-4 py-2 text-center capitalize ${cam == selected ? "bg-secondary rounded-lg" : ""}`}
className={`mx-4 w-full py-2 text-center capitalize ${cam == selected ? "rounded-lg bg-secondary" : ""}`}
onClick={() => {
onSelectCamera(cam);
setCameraDrawer(false);

View File

@ -112,22 +112,22 @@ export default function MobileReviewSettingsDrawer({
let content;
if (drawerMode == "select") {
content = (
<div className="w-full p-4 flex flex-col gap-2">
<div className="flex w-full flex-col gap-2 p-4">
{features.includes("export") && (
<Button
className="w-full flex justify-center items-center gap-2"
className="flex w-full items-center justify-center gap-2"
onClick={() => {
setDrawerMode("export");
setMode("select");
}}
>
<FaArrowDown className="p-1 fill-secondary bg-secondary-foreground rounded-md" />
<FaArrowDown className="rounded-md bg-secondary-foreground fill-secondary p-1" />
Export
</Button>
)}
{features.includes("calendar") && (
<Button
className="w-full flex justify-center items-center gap-2"
className="flex w-full items-center justify-center gap-2"
variant={filter?.after ? "select" : "default"}
onClick={() => setDrawerMode("calendar")}
>
@ -139,7 +139,7 @@ export default function MobileReviewSettingsDrawer({
)}
{features.includes("filter") && (
<Button
className="w-full flex justify-center items-center gap-2"
className="flex w-full items-center justify-center gap-2"
variant={filter?.labels ? "select" : "default"}
onClick={() => setDrawerMode("filter")}
>
@ -177,8 +177,8 @@ export default function MobileReviewSettingsDrawer({
);
} else if (drawerMode == "calendar") {
content = (
<div className="w-full flex flex-col">
<div className="w-full h-8 relative">
<div className="flex w-full flex-col">
<div className="relative h-8 w-full">
<div
className="absolute left-0 text-selected"
onClick={() => setDrawerMode("select")}
@ -205,7 +205,7 @@ export default function MobileReviewSettingsDrawer({
}}
/>
<SelectSeparator />
<div className="p-2 flex justify-center items-center">
<div className="flex items-center justify-center p-2">
<Button
onClick={() => {
onUpdateFilter({
@ -222,8 +222,8 @@ export default function MobileReviewSettingsDrawer({
);
} else if (drawerMode == "filter") {
content = (
<div className="w-full h-auto overflow-y-auto flex flex-col">
<div className="w-full h-8 mb-2 relative">
<div className="flex h-auto w-full flex-col overflow-y-auto">
<div className="relative mb-2 h-8 w-full">
<div
className="absolute left-0 text-selected"
onClick={() => setDrawerMode("select")}
@ -256,7 +256,7 @@ export default function MobileReviewSettingsDrawer({
return (
<>
<SaveExportOverlay
className="absolute top-8 left-1/2 -translate-x-1/2 z-50 pointer-events-none"
className="pointer-events-none absolute left-1/2 top-8 z-50 -translate-x-1/2"
show={mode == "timeline"}
onSave={() => onStartExport()}
onCancel={() => setMode("none")}
@ -281,7 +281,7 @@ export default function MobileReviewSettingsDrawer({
/>
</Button>
</DrawerTrigger>
<DrawerContent className="max-h-[80dvh] overflow-hidden flex flex-col items-center gap-2 px-4 pb-4 mx-1 rounded-t-2xl">
<DrawerContent className="mx-1 flex max-h-[80dvh] flex-col items-center gap-2 overflow-hidden rounded-t-2xl px-4 pb-4">
{content}
</DrawerContent>
</Drawer>

View File

@ -26,9 +26,9 @@ export default function MobileTimelineDrawer({
<FaFlag className="text-secondary-foreground" />
</Button>
</DrawerTrigger>
<DrawerContent className="max-h-[75dvh] overflow-hidden flex flex-col items-center gap-2 px-4 pb-4 mx-1 rounded-t-2xl">
<DrawerContent className="mx-1 flex max-h-[75dvh] flex-col items-center gap-2 overflow-hidden rounded-t-2xl px-4 pb-4">
<div
className={`w-full mx-4 py-2 text-center capitalize ${selected == "timeline" ? "bg-secondary rounded-lg" : ""}`}
className={`mx-4 w-full py-2 text-center capitalize ${selected == "timeline" ? "rounded-lg bg-secondary" : ""}`}
onClick={() => {
onSelect("timeline");
setDrawer(false);
@ -37,7 +37,7 @@ export default function MobileTimelineDrawer({
Timeline
</div>
<div
className={`w-full mx-4 py-2 text-center capitalize ${selected == "events" ? "bg-secondary rounded-lg" : ""}`}
className={`mx-4 w-full py-2 text-center capitalize ${selected == "events" ? "rounded-lg bg-secondary" : ""}`}
onClick={() => {
onSelect("events");
setDrawer(false);

View File

@ -67,7 +67,7 @@ function ReviewActivityDay({ reviewSummary, day }: ReviewActivityDayProps) {
}, [reviewSummary, day]);
return (
<div className="flex flex-col justify-center items-center">
<div className="flex flex-col items-center justify-center">
{day.getDate()}
{dayActivity != "none" && (
<FaCircle

View File

@ -17,9 +17,9 @@ export default function SaveExportOverlay({
return (
<div className={className}>
<div
className={`flex justify-center px-2 gap-2 items-center pointer-events-auto rounded-lg ${
show ? "animate-in slide-in-from-top duration-500" : "invisible"
} text-center mt-5 mx-auto`}
className={`pointer-events-auto flex items-center justify-center gap-2 rounded-lg px-2 ${
show ? "duration-500 animate-in slide-in-from-top" : "invisible"
} mx-auto mt-5 text-center`}
>
<Button
className="flex items-center gap-1"

View File

@ -84,12 +84,12 @@ export default function TimelineEventOverlay({
}}
>
{timeline.class_type == "entered_zone" ? (
<div className="absolute w-2 h-2 bg-yellow-500 left-[50%] -translate-x-1/2 translate-y-3/4 bottom-0" />
<div className="absolute bottom-0 left-[50%] h-2 w-2 -translate-x-1/2 translate-y-3/4 bg-yellow-500" />
) : null}
</div>
{isHovering && (
<div
className="absolute bg-white dark:bg-slate-800 p-4 block text-black dark:text-white text-lg"
className="absolute block bg-white p-4 text-lg text-black dark:bg-slate-800 dark:text-white"
style={getHoverStyle()}
>
<div>{`Area: ${getObjectArea()} px`}</div>

View File

@ -33,7 +33,7 @@ export default function VainfoDialog({
<DialogTitle>Vainfo Output</DialogTitle>
</DialogHeader>
{vainfo ? (
<div className="mb-2 max-h-96 whitespace-pre-line overflow-y-scroll">
<div className="mb-2 max-h-96 overflow-y-scroll whitespace-pre-line">
<div>Return Code: {vainfo.return_code}</div>
<br />
<div>Process {vainfo.return_code == 0 ? "Output" : "Error"}:</div>

View File

@ -23,7 +23,7 @@ export default function BirdseyeLivePlayer({
if (liveMode == "webrtc") {
player = (
<WebRtcPlayer
className={`rounded-lg md:rounded-2xl size-full`}
className={`size-full rounded-lg md:rounded-2xl`}
camera="birdseye"
/>
);
@ -31,7 +31,7 @@ export default function BirdseyeLivePlayer({
if ("MediaSource" in window || "ManagedMediaSource" in window) {
player = (
<MSEPlayer
className={`rounded-lg md:rounded-2xl size-full`}
className={`size-full rounded-lg md:rounded-2xl`}
camera="birdseye"
/>
);
@ -46,7 +46,7 @@ export default function BirdseyeLivePlayer({
} else if (liveMode == "jsmpeg") {
player = (
<JSMpegPlayer
className="size-full flex justify-center rounded-lg md:rounded-2xl overflow-hidden"
className="flex size-full justify-center overflow-hidden rounded-lg md:rounded-2xl"
camera="birdseye"
width={birdseyeConfig.width}
height={birdseyeConfig.height}
@ -59,13 +59,13 @@ export default function BirdseyeLivePlayer({
return (
<div
className={cn(
"relative flex justify-center w-full cursor-pointer",
"relative flex w-full cursor-pointer justify-center",
className,
)}
onClick={onClick}
>
<div className="absolute top-0 inset-x-0 rounded-lg md:rounded-2xl z-10 w-full h-[30%] bg-gradient-to-b from-black/20 to-transparent pointer-events-none"></div>
<div className="absolute bottom-0 inset-x-0 rounded-lg md:rounded-2xl z-10 w-full h-[10%] bg-gradient-to-t from-black/20 to-transparent pointer-events-none"></div>
<div className="pointer-events-none absolute inset-x-0 top-0 z-10 h-[30%] w-full rounded-lg bg-gradient-to-b from-black/20 to-transparent md:rounded-2xl"></div>
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 h-[10%] w-full rounded-lg bg-gradient-to-t from-black/20 to-transparent md:rounded-2xl"></div>
<div className="size-full">{player}</div>
</div>
);

View File

@ -153,7 +153,7 @@ export default function HlsVideoPlayer({
return (
<TransformWrapper minScale={1.0}>
<VideoControls
className="absolute bottom-5 left-1/2 -translate-x-1/2 z-50"
className="absolute bottom-5 left-1/2 z-50 -translate-x-1/2"
video={videoRef.current}
isPlaying={isPlaying}
show={visible && (controls || controlsOpen)}
@ -231,7 +231,7 @@ export default function HlsVideoPlayer({
>
<video
ref={videoRef}
className={`size-full bg-black rounded-lg md:rounded-2xl ${loadedMetadata ? "" : "invisible"}`}
className={`size-full rounded-lg bg-black md:rounded-2xl ${loadedMetadata ? "" : "invisible"}`}
preload="auto"
autoPlay
controls={false}

View File

@ -103,7 +103,7 @@ export default function LivePlayer({
if (liveMode == "webrtc") {
player = (
<WebRtcPlayer
className={`rounded-lg md:rounded-2xl size-full ${liveReady ? "" : "hidden"}`}
className={`size-full rounded-lg md:rounded-2xl ${liveReady ? "" : "hidden"}`}
camera={cameraConfig.live.stream_name}
playbackEnabled={cameraActive}
audioEnabled={playAudio}
@ -117,7 +117,7 @@ export default function LivePlayer({
if ("MediaSource" in window || "ManagedMediaSource" in window) {
player = (
<MSEPlayer
className={`rounded-lg md:rounded-2xl size-full ${liveReady ? "" : "hidden"}`}
className={`size-full rounded-lg md:rounded-2xl ${liveReady ? "" : "hidden"}`}
camera={cameraConfig.live.stream_name}
playbackEnabled={cameraActive}
audioEnabled={playAudio}
@ -137,7 +137,7 @@ export default function LivePlayer({
} else if (liveMode == "jsmpeg") {
player = (
<JSMpegPlayer
className="size-full flex justify-center rounded-lg md:rounded-2xl overflow-hidden"
className="flex size-full justify-center overflow-hidden rounded-lg md:rounded-2xl"
camera={cameraConfig.live.stream_name}
width={cameraConfig.detect.width}
height={cameraConfig.detect.height}
@ -154,17 +154,17 @@ export default function LivePlayer({
className={cn(
"relative flex justify-center",
liveMode === "jsmpeg" ? "size-full" : "w-full",
"outline cursor-pointer",
"cursor-pointer outline",
activeTracking
? "outline-severity_alert outline-3 rounded-lg md:rounded-2xl shadow-severity_alert"
? "outline-3 rounded-lg shadow-severity_alert outline-severity_alert md:rounded-2xl"
: "outline-0 outline-background",
"transition-all duration-500",
className,
)}
onClick={onClick}
>
<div className="absolute top-0 inset-x-0 rounded-lg md:rounded-2xl z-10 w-full h-[30%] bg-gradient-to-b from-black/20 to-transparent pointer-events-none"></div>
<div className="absolute bottom-0 inset-x-0 rounded-lg md:rounded-2xl z-10 w-full h-[10%] bg-gradient-to-t from-black/20 to-transparent pointer-events-none"></div>
<div className="pointer-events-none absolute inset-x-0 top-0 z-10 h-[30%] w-full rounded-lg bg-gradient-to-b from-black/20 to-transparent md:rounded-2xl"></div>
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 h-[10%] w-full rounded-lg bg-gradient-to-t from-black/20 to-transparent md:rounded-2xl"></div>
{player}
{objects.length > 0 && (
@ -172,9 +172,9 @@ export default function LivePlayer({
<Tooltip>
<div className="flex">
<TooltipTrigger asChild>
<div className="mx-3 pb-1 text-white text-sm">
<div className="mx-3 pb-1 text-sm text-white">
<Chip
className={`flex items-start justify-between space-x-1 bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500 z-0`}
className={`z-0 flex items-start justify-between space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500`}
>
{[
...new Set([
@ -226,7 +226,7 @@ export default function LivePlayer({
<div className="absolute right-2 top-2 size-4">
{activeMotion && (
<MdCircle className="size-2 drop-shadow-md shadow-danger text-danger animate-pulse" />
<MdCircle className="size-2 animate-pulse text-danger shadow-danger drop-shadow-md" />
)}
</div>
</div>

View File

@ -240,7 +240,7 @@ function PreviewVideoPlayer({
return (
<div
className={cn(
"relative rounded-lg md:rounded-2xl w-full flex justify-center bg-black overflow-hidden",
"relative flex w-full justify-center overflow-hidden rounded-lg bg-black md:rounded-2xl",
onClick && "cursor-pointer",
className,
)}
@ -288,11 +288,11 @@ function PreviewVideoPlayer({
)}
</video>
{cameraPreviews && !currentPreview && (
<div className="absolute inset-0 text-white rounded-lg md:rounded-2xl flex justify-center items-center">
<div className="absolute inset-0 flex items-center justify-center rounded-lg text-white md:rounded-2xl">
No Preview Found
</div>
)}
{firstLoad && <Skeleton className="absolute size-full aspect-video" />}
{firstLoad && <Skeleton className="absolute aspect-video size-full" />}
</div>
);
}
@ -482,7 +482,7 @@ function PreviewFramesPlayer({
return (
<div
className={cn(
"relative w-full flex justify-center",
"relative flex w-full justify-center",
className,
onClick && "cursor-pointer",
)}
@ -490,15 +490,15 @@ function PreviewFramesPlayer({
>
<img
ref={imgRef}
className={`size-full object-contain rounded-lg md:rounded-2xl bg-black`}
className={`size-full rounded-lg bg-black object-contain md:rounded-2xl`}
onLoad={onImageLoaded}
/>
{previewFrames?.length === 0 && (
<div className="absolute inset-x-0 top-1/2 -y-translate-1/2 bg-black text-white rounded-lg md:rounded-2xl align-center text-center">
<div className="-y-translate-1/2 align-center absolute inset-x-0 top-1/2 rounded-lg bg-black text-center text-white md:rounded-2xl">
No Preview Found
</div>
)}
{firstLoad && <Skeleton className="absolute size-full aspect-video" />}
{firstLoad && <Skeleton className="absolute aspect-video size-full" />}
</div>
);
}

View File

@ -207,7 +207,7 @@ export default function PreviewThumbnailPlayer({
<div className={`${imgLoaded ? "visible" : "invisible"}`}>
<img
ref={imgRef}
className={`size-full transition-opacity select-none ${
className={`size-full select-none transition-opacity ${
playingBack ? "opacity-0" : "opacity-100"
}`}
style={
@ -234,12 +234,12 @@ export default function PreviewThumbnailPlayer({
onMouseLeave={() => setTooltipHovering(false)}
>
<TooltipTrigger asChild>
<div className="mx-3 pb-1 text-white text-sm">
<div className="mx-3 pb-1 text-sm text-white">
{(review.severity == "alert" ||
review.severity == "detection") && (
<>
<Chip
className={`flex items-start justify-between space-x-1 ${playingBack ? "hidden" : ""} bg-gradient-to-br ${review.has_been_reviewed ? "from-green-600 to-green-700 bg-green-600" : "from-gray-400 to-gray-500 bg-gray-500"} z-0`}
className={`flex items-start justify-between space-x-1 ${playingBack ? "hidden" : ""} bg-gradient-to-br ${review.has_been_reviewed ? "bg-green-600 from-green-600 to-green-700" : "bg-gray-500 from-gray-400 to-gray-500"} z-0`}
>
{review.data.objects.sort().map((object) => {
return getIconForLabel(object, "size-3 text-white");
@ -273,9 +273,9 @@ export default function PreviewThumbnailPlayer({
</div>
{!playingBack && (
<>
<div className="absolute top-0 inset-x-0 rounded-t-l z-10 w-full h-[30%] bg-gradient-to-b from-black/60 to-transparent pointer-events-none"></div>
<div className="absolute bottom-0 inset-x-0 rounded-b-l z-10 w-full h-[20%] bg-gradient-to-t from-black/60 to-transparent pointer-events-none">
<div className="flex h-full justify-between items-end mx-3 pb-1 text-white text-sm">
<div className="rounded-t-l pointer-events-none absolute inset-x-0 top-0 z-10 h-[30%] w-full bg-gradient-to-b from-black/60 to-transparent"></div>
<div className="rounded-b-l pointer-events-none absolute inset-x-0 bottom-0 z-10 h-[20%] w-full bg-gradient-to-t from-black/60 to-transparent">
<div className="mx-3 flex h-full items-end justify-between pb-1 text-sm text-white">
{review.end_time ? (
<TimeAgo time={review.start_time * 1000} dense />
) : (
@ -557,10 +557,10 @@ export function VideoPreview({
);
return (
<div className="relative size-full aspect-video bg-black">
<div className="relative aspect-video size-full bg-black">
<video
ref={playerRef}
className="size-full aspect-video bg-black pointer-events-none"
className="pointer-events-none aspect-video size-full bg-black"
autoPlay
playsInline
preload="auto"
@ -738,9 +738,9 @@ export function InProgressPreview({
}
return (
<div className="relative size-full flex items-center bg-black">
<div className="relative flex size-full items-center bg-black">
<img
className="size-full object-contain pointer-events-none"
className="pointer-events-none size-full object-contain"
src={`${apiHost}api/preview/${previewFrames[key]}/thumbnail.webp`}
onLoad={handleLoad}
/>

View File

@ -171,16 +171,16 @@ export default function VideoControls({
return (
<div
className={cn(
"w-auto px-4 py-2 flex sm:flex-nowrap justify-between items-center gap-4 sm:gap-8 text-primary z-50 bg-background/60 rounded-lg",
"z-50 flex w-auto items-center justify-between gap-4 rounded-lg bg-background/60 px-4 py-2 text-primary sm:flex-nowrap sm:gap-8",
className,
isMobileOnly &&
Object.values(features).filter((feat) => feat).length >
MIN_ITEMS_WRAP &&
"flex-wrap min-w-[75%]",
"min-w-[75%] flex-wrap",
)}
>
{video && features.volume && (
<div className="flex justify-normal items-center gap-2 cursor-pointer">
<div className="flex cursor-pointer items-center justify-normal gap-2">
<VolumeIcon
className="size-5"
onClick={(e: React.MouseEvent) => {
@ -208,9 +208,9 @@ export default function VideoControls({
)}
<div className="cursor-pointer" onClick={onTogglePlay}>
{isPlaying ? (
<LuPause className="size-5 text-primary fill-primary" />
<LuPause className="size-5 fill-primary text-primary" />
) : (
<LuPlay className="size-5 text-primary fill-primary" />
<LuPlay className="size-5 fill-primary text-primary" />
)}
</div>
{features.seek && (

View File

@ -53,18 +53,18 @@ export default function General() {
return (
<>
<div className="flex flex-col md:flex-row size-full">
<div className="flex size-full flex-col md:flex-row">
<Toaster position="top-center" closeButton={true} />
<div className="flex flex-col h-full w-full overflow-y-auto mt-2 md:mt-0 mb-10 md:mb-0 order-last md:order-none md:mr-2 rounded-lg border-secondary-foreground border-[1px] p-2 bg-background_alt">
<div className="order-last mb-10 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0">
<Heading as="h3" className="my-2">
General Settings
</Heading>
<div className="flex flex-col w-full space-y-6">
<div className="flex w-full flex-col space-y-6">
<div className="mt-2 space-y-6">
<div className="space-y-0.5">
<div className="text-md">Stored Layouts</div>
<div className="text-sm text-muted-foreground my-2">
<div className="my-2 text-sm text-muted-foreground">
<p>
The layout of cameras in a camera group can be
dragged/resized. The positions are stored in your browser's
@ -72,11 +72,11 @@ export default function General() {
</p>
</div>
</div>
<div className="flex flex-row justify-start items-center gap-2">
<div className="flex flex-row items-center justify-start gap-2">
<Button onClick={clearStoredLayouts}>Clear All Layouts</Button>
</div>
</div>
<Separator className="flex my-2 bg-secondary" />
<Separator className="my-2 flex bg-secondary" />
<div className="mt-2 space-y-6">
<div className="space-y-0.5">
<div className="text-md">Default Playback Rate</div>
@ -110,13 +110,13 @@ export default function General() {
<div className="mt-2 space-y-6">
<div className="space-y-0.5">
<div className="text-md">Low Data Mode</div>
<div className="text-sm text-muted-foreground my-2">
<div className="my-2 text-sm text-muted-foreground">
<p>
Not yet implemented. <em>Default: disabled</em>
</p>
</div>
</div>
<div className="flex flex-row justify-start items-center gap-2">
<div className="flex flex-row items-center justify-start gap-2">
<Switch
id="lowdata"
checked={false}

View File

@ -370,9 +370,9 @@ export default function MasksAndZones({
return (
<>
{cameraConfig && editingPolygons && (
<div className="flex flex-col md:flex-row size-full">
<div className="flex size-full flex-col md:flex-row">
<Toaster position="top-center" closeButton={true} />
<div className="flex flex-col h-full w-full overflow-y-auto mt-2 md:mt-0 mb-10 md:mb-0 md:w-3/12 order-last md:order-none md:mr-2 rounded-lg border-secondary-foreground border-[1px] p-2 bg-background_alt">
<div className="order-last mb-10 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0 md:w-3/12">
{editPane == "zone" && (
<ZoneEditPane
polygons={editingPolygons}
@ -417,17 +417,17 @@ export default function MasksAndZones({
<Heading as="h3" className="my-2">
Masks / Zones
</Heading>
<div className="flex flex-col w-full">
<div className="flex w-full flex-col">
{(selectedZoneMask === undefined ||
selectedZoneMask.includes("zone" as PolygonType)) && (
<div className="mt-0 pt-0 last:pb-3 last:border-b-[1px] last:border-secondary">
<div className="flex flex-row justify-between items-center my-3">
<div className="mt-0 pt-0 last:border-b-[1px] last:border-secondary last:pb-3">
<div className="my-3 flex flex-row items-center justify-between">
<HoverCard>
<HoverCardTrigger asChild>
<div className="text-md cursor-default">Zones</div>
</HoverCardTrigger>
<HoverCardContent>
<div className="flex flex-col gap-2 text-sm text-primary-variant my-2">
<div className="my-2 flex flex-col gap-2 text-sm text-primary-variant">
<p>
Zones allow you to define a specific area of the
frame so you can determine whether or not an
@ -441,7 +441,7 @@ export default function MasksAndZones({
className="inline"
>
Documentation{" "}
<LuExternalLink className="size-3 ml-2 inline-flex" />
<LuExternalLink className="ml-2 inline-flex size-3" />
</Link>
</div>
</div>
@ -451,7 +451,7 @@ export default function MasksAndZones({
<TooltipTrigger asChild>
<Button
variant="secondary"
className="size-6 p-1 rounded-md text-background bg-secondary-foreground"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
onClick={() => {
setEditPane("zone");
handleNewPolygon("zone");
@ -485,8 +485,8 @@ export default function MasksAndZones({
selectedZoneMask.includes(
"motion_mask" as PolygonType,
)) && (
<div className="first:mt-0 mt-3 first:pt-0 pt-3 last:pb-3 border-t-[1px] last:border-b-[1px] first:border-transparent border-secondary">
<div className="flex flex-row justify-between items-center my-3">
<div className="mt-3 border-t-[1px] border-secondary pt-3 first:mt-0 first:border-transparent first:pt-0 last:border-b-[1px] last:pb-3">
<div className="my-3 flex flex-row items-center justify-between">
<HoverCard>
<HoverCardTrigger asChild>
<div className="text-md cursor-default">
@ -494,7 +494,7 @@ export default function MasksAndZones({
</div>
</HoverCardTrigger>
<HoverCardContent>
<div className="flex flex-col gap-2 text-sm text-primary-variant my-2">
<div className="my-2 flex flex-col gap-2 text-sm text-primary-variant">
<p>
Motion masks are used to prevent unwanted types
of motion from triggering detection. Over
@ -509,7 +509,7 @@ export default function MasksAndZones({
className="inline"
>
Documentation{" "}
<LuExternalLink className="size-3 ml-2 inline-flex" />
<LuExternalLink className="ml-2 inline-flex size-3" />
</Link>
</div>
</div>
@ -519,7 +519,7 @@ export default function MasksAndZones({
<TooltipTrigger asChild>
<Button
variant="secondary"
className="size-6 p-1 rounded-md text-background bg-secondary-foreground"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
onClick={() => {
setEditPane("motion_mask");
handleNewPolygon("motion_mask");
@ -555,8 +555,8 @@ export default function MasksAndZones({
selectedZoneMask.includes(
"object_mask" as PolygonType,
)) && (
<div className="first:mt-0 mt-3 first:pt-0 pt-3 last:pb-3 border-t-[1px] last:border-b-[1px] first:border-transparent border-secondary">
<div className="flex flex-row justify-between items-center my-3">
<div className="mt-3 border-t-[1px] border-secondary pt-3 first:mt-0 first:border-transparent first:pt-0 last:border-b-[1px] last:pb-3">
<div className="my-3 flex flex-row items-center justify-between">
<HoverCard>
<HoverCardTrigger asChild>
<div className="text-md cursor-default">
@ -564,7 +564,7 @@ export default function MasksAndZones({
</div>
</HoverCardTrigger>
<HoverCardContent>
<div className="flex flex-col gap-2 text-sm text-primary-variant my-2">
<div className="my-2 flex flex-col gap-2 text-sm text-primary-variant">
<p>
Object filter masks are used to filter out false
positives for a given object type based on
@ -578,7 +578,7 @@ export default function MasksAndZones({
className="inline"
>
Documentation{" "}
<LuExternalLink className="size-3 ml-2 inline-flex" />
<LuExternalLink className="ml-2 inline-flex size-3" />
</Link>
</div>
</div>
@ -588,7 +588,7 @@ export default function MasksAndZones({
<TooltipTrigger asChild>
<Button
variant="secondary"
className="size-6 p-1 rounded-md text-background bg-secondary-foreground"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
onClick={() => {
setEditPane("object_mask");
handleNewPolygon("object_mask");
@ -626,9 +626,9 @@ export default function MasksAndZones({
</div>
<div
ref={containerRef}
className="flex md:w-7/12 md:grow md:h-dvh max-h-[50%] md:max-h-full"
className="flex max-h-[50%] md:h-dvh md:max-h-full md:w-7/12 md:grow"
>
<div className="flex flex-row justify-center mx-auto size-full">
<div className="mx-auto flex size-full flex-row justify-center">
{cameraConfig &&
scaledWidth &&
scaledHeight &&

View File

@ -187,7 +187,7 @@ export default function MotionMaskEditPane({
<Heading as="h3" className="my-2">
{polygon.name.length ? "Edit" : "New"} Motion Mask
</Heading>
<div className="text-sm text-muted-foreground my-2">
<div className="my-2 text-sm text-muted-foreground">
<p>
Motion masks are used to prevent unwanted types of motion from
triggering detection. Over masking will make it more difficult for
@ -196,7 +196,7 @@ export default function MotionMaskEditPane({
</div>
<Separator className="my-3 bg-secondary" />
{polygons && activePolygonIndex !== undefined && (
<div className="flex flex-row my-2 text-sm w-full justify-between">
<div className="my-2 flex w-full flex-row justify-between text-sm">
<div className="my-1 inline-flex">
{polygons[activePolygonIndex].points.length}{" "}
{polygons[activePolygonIndex].points.length > 1 ||
@ -223,7 +223,7 @@ export default function MotionMaskEditPane({
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 flex flex-col flex-1"
className="flex flex-1 flex-col space-y-6"
>
<FormField
control={form.control}
@ -243,7 +243,7 @@ export default function MotionMaskEditPane({
</FormItem>
)}
/>
<div className="flex flex-col flex-1 justify-end">
<div className="flex flex-1 flex-col justify-end">
<div className="flex flex-row gap-2 pt-5">
<Button className="flex flex-1" onClick={onCancel}>
Cancel

View File

@ -173,13 +173,13 @@ export default function MotionTuner({
}
return (
<div className="flex flex-col md:flex-row size-full">
<div className="flex size-full flex-col md:flex-row">
<Toaster position="top-center" closeButton={true} />
<div className="flex flex-col h-full w-full overflow-y-auto mt-2 md:mt-0 mb-10 md:mb-0 md:w-3/12 order-last md:order-none md:mr-2 rounded-lg border-secondary-foreground border-[1px] p-2 bg-background_alt">
<div className="order-last mb-10 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0 md:w-3/12">
<Heading as="h3" className="my-2">
Motion Detection Tuner
</Heading>
<div className="text-sm text-muted-foreground my-3 space-y-3">
<div className="my-3 space-y-3 text-sm text-muted-foreground">
<p>
Frigate uses motion detection as a first line check to see if there
is anything happening in the frame worth checking with object
@ -194,18 +194,18 @@ export default function MotionTuner({
className="inline"
>
Read the Motion Tuning Guide{" "}
<LuExternalLink className="size-3 ml-2 inline-flex" />
<LuExternalLink className="ml-2 inline-flex size-3" />
</Link>
</div>
</div>
<Separator className="flex my-2 bg-secondary" />
<div className="flex flex-col w-full space-y-6">
<Separator className="my-2 flex bg-secondary" />
<div className="flex w-full flex-col space-y-6">
<div className="mt-2 space-y-6">
<div className="space-y-0.5">
<Label htmlFor="motion-threshold" className="text-md">
Threshold
</Label>
<div className="text-sm text-muted-foreground my-2">
<div className="my-2 text-sm text-muted-foreground">
<p>
The threshold value dictates how much of a change in a pixel's
luminance is required to be considered motion.{" "}
@ -226,7 +226,7 @@ export default function MotionTuner({
handleMotionConfigChange({ threshold: value[0] });
}}
/>
<div className="text-lg ml-6 mr-2 flex align-center">
<div className="align-center ml-6 mr-2 flex text-lg">
{motionSettings.threshold}
</div>
</div>
@ -236,7 +236,7 @@ export default function MotionTuner({
<Label htmlFor="motion-threshold" className="text-md">
Contour Area
</Label>
<div className="text-sm text-muted-foreground my-2">
<div className="my-2 text-sm text-muted-foreground">
<p>
The contour area value is used to decide which groups of
changed pixels qualify as motion. <em>Default: 10</em>
@ -256,12 +256,12 @@ export default function MotionTuner({
handleMotionConfigChange({ contour_area: value[0] });
}}
/>
<div className="text-lg ml-6 mr-2 flex align-center">
<div className="align-center ml-6 mr-2 flex text-lg">
{motionSettings.contour_area}
</div>
</div>
</div>
<Separator className="flex my-2 bg-secondary" />
<Separator className="my-2 flex bg-secondary" />
<div className="flex flex-row items-center justify-between">
<div className="space-y-0.5">
<Label htmlFor="improve-contrast">Improve Contrast</Label>
@ -280,7 +280,7 @@ export default function MotionTuner({
/>
</div>
</div>
<div className="flex flex-col flex-1 justify-end">
<div className="flex flex-1 flex-col justify-end">
<div className="flex flex-row gap-2 pt-5">
<Button className="flex flex-1" onClick={onCancel}>
Reset
@ -305,7 +305,7 @@ export default function MotionTuner({
</div>
{cameraConfig ? (
<div className="flex md:w-7/12 md:grow md:h-dvh md:max-h-full">
<div className="flex md:h-dvh md:max-h-full md:w-7/12 md:grow">
<div className="size-full min-h-10">
<AutoUpdatingCameraImage
camera={cameraConfig.name}

View File

@ -249,7 +249,7 @@ export default function ObjectMaskEditPane({
<Heading as="h3" className="my-2">
{polygon.name.length ? "Edit" : "New"} Object Mask
</Heading>
<div className="text-sm text-muted-foreground my-2">
<div className="my-2 text-sm text-muted-foreground">
<p>
Object filter masks are used to filter out false positives for a given
object type based on location.
@ -257,7 +257,7 @@ export default function ObjectMaskEditPane({
</div>
<Separator className="my-3 bg-secondary" />
{polygons && activePolygonIndex !== undefined && (
<div className="flex flex-row my-2 text-sm w-full justify-between">
<div className="my-2 flex w-full flex-row justify-between text-sm">
<div className="my-1 inline-flex">
{polygons[activePolygonIndex].points.length}{" "}
{polygons[activePolygonIndex].points.length > 1 ||
@ -284,7 +284,7 @@ export default function ObjectMaskEditPane({
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 flex flex-col flex-1"
className="flex flex-1 flex-col space-y-6"
>
<div>
<FormField
@ -332,7 +332,7 @@ export default function ObjectMaskEditPane({
)}
/>
</div>
<div className="flex flex-col flex-1 justify-end">
<div className="flex flex-1 flex-col justify-end">
<div className="flex flex-row gap-2 pt-5">
<Button className="flex flex-1" onClick={onCancel}>
Cancel

View File

@ -111,13 +111,13 @@ export default function ObjectSettings({
}
return (
<div className="flex flex-col md:flex-row size-full">
<div className="flex size-full flex-col md:flex-row">
<Toaster position="top-center" closeButton={true} />
<div className="flex flex-col h-full w-full overflow-y-auto mt-2 md:mt-0 mb-10 md:mb-0 md:w-3/12 order-last md:order-none md:mr-2 rounded-lg border-secondary-foreground border-[1px] p-2 bg-background_alt">
<div className="order-last mb-10 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0 md:w-3/12">
<Heading as="h3" className="my-2">
Debug
</Heading>
<div className="text-sm text-muted-foreground mb-5 space-y-3">
<div className="mb-5 space-y-3 text-sm text-muted-foreground">
<p>
Frigate uses your detectors{" "}
{config
@ -142,17 +142,17 @@ export default function ObjectSettings({
<TabsTrigger value="objectlist">Object List</TabsTrigger>
</TabsList>
<TabsContent value="debug">
<div className="flex flex-col w-full space-y-6">
<div className="flex w-full flex-col space-y-6">
<div className="mt-2 space-y-6">
<div className="my-2.5 flex flex-col gap-2.5">
{DEBUG_OPTIONS.map(({ param, title, description }) => (
<div
key={param}
className="flex flex-row w-full justify-between items-center"
className="flex w-full flex-row items-center justify-between"
>
<div className="flex flex-col mb-2">
<div className="mb-2 flex flex-col">
<Label
className="w-full text-primary capitalize cursor-pointer mb-2"
className="mb-2 w-full cursor-pointer capitalize text-primary"
htmlFor={param}
>
{title}
@ -183,7 +183,7 @@ export default function ObjectSettings({
</div>
{cameraConfig ? (
<div className="flex md:w-7/12 md:grow md:h-dvh md:max-h-full">
<div className="flex md:h-dvh md:max-h-full md:w-7/12 md:grow">
<div className="size-full min-h-10">
<AutoUpdatingCameraImage
camera={cameraConfig.name}
@ -222,15 +222,15 @@ function ObjectList(objects?: ObjectType[]) {
);
return (
<div className="flex flex-col w-full overflow-y-auto">
<div className="flex w-full flex-col overflow-y-auto">
{objects && objects.length > 0 ? (
objects.map((obj) => {
return (
<Card className="text-sm p-2 mb-1" key={obj.id}>
<Card className="mb-1 p-2 text-sm" key={obj.id}>
<div className="flex flex-row items-center gap-3 pb-1">
<div className="flex flex-row flex-1 items-center justify-start p-3 pl-1">
<div className="flex flex-1 flex-row items-center justify-start p-3 pl-1">
<div
className="p-2 rounded-lg"
className="rounded-lg p-2"
style={{
backgroundColor: obj.stationary
? "rgb(110,110,110)"
@ -243,10 +243,10 @@ function ObjectList(objects?: ObjectType[]) {
{capitalizeFirstLetter(obj.label)}
</div>
</div>
<div className="flex flex-row w-8/12 items-end justify-end">
<div className="mr-2 text-md w-1/3">
<div className="flex w-8/12 flex-row items-end justify-end">
<div className="text-md mr-2 w-1/3">
<div className="flex flex-col items-end justify-end">
<p className="text-sm mb-1.5 text-primary-variant">
<p className="mb-1.5 text-sm text-primary-variant">
Score
</p>
{obj.score
@ -255,17 +255,17 @@ function ObjectList(objects?: ObjectType[]) {
%
</div>
</div>
<div className="mr-2 text-md w-1/3">
<div className="text-md mr-2 w-1/3">
<div className="flex flex-col items-end justify-end">
<p className="text-sm mb-1.5 text-primary-variant">
<p className="mb-1.5 text-sm text-primary-variant">
Ratio
</p>
{obj.ratio ? obj.ratio.toFixed(2).toString() : "-"}
</div>
</div>
<div className="mr-2 text-md w-1/3">
<div className="text-md mr-2 w-1/3">
<div className="flex flex-col items-end justify-end">
<p className="text-sm mb-1.5 text-primary-variant">
<p className="mb-1.5 text-sm text-primary-variant">
Area
</p>
{obj.area ? obj.area.toString() : "-"}

View File

@ -73,7 +73,7 @@ export default function PolygonEditControls({
<TooltipTrigger asChild>
<Button
variant="default"
className="size-6 p-1 rounded-md"
className="size-6 rounded-md p-1"
disabled={!polygons[activePolygonIndex].points.length}
onClick={undo}
>
@ -86,7 +86,7 @@ export default function PolygonEditControls({
<TooltipTrigger asChild>
<Button
variant="default"
className="size-6 p-1 rounded-md"
className="size-6 rounded-md p-1"
disabled={!polygons[activePolygonIndex].points.length}
onClick={reset}
>

View File

@ -206,7 +206,7 @@ export default function PolygonItem({
<div
key={index}
className="flex p-1 rounded-lg flex-row items-center justify-between my-1.5 transition-background duration-100"
className="transition-background my-1.5 flex flex-row items-center justify-between rounded-lg p-1 duration-100"
data-index={index}
onMouseEnter={() => setHoveredPolygonIndex(index)}
onMouseLeave={() => setHoveredPolygonIndex(null)}
@ -226,7 +226,7 @@ export default function PolygonItem({
>
{PolygonItemIcon && (
<PolygonItemIcon
className="size-5 mr-2"
className="mr-2 size-5"
style={{
fill: toRGBColorString(polygon.color, true),
color: toRGBColorString(polygon.color, true),
@ -285,7 +285,7 @@ export default function PolygonItem({
</>
)}
{!isMobile && hoveredPolygonIndex === index && (
<div className="flex flex-row gap-2 items-center">
<div className="flex flex-row items-center gap-2">
<Tooltip>
<TooltipTrigger asChild>
<IconWrapper
@ -319,7 +319,7 @@ export default function PolygonItem({
icon={HiTrash}
className={`size-[15px] cursor-pointer ${
hoveredPolygonIndex === index &&
"text-primary-variant fill-primary-variant"
"fill-primary-variant text-primary-variant"
}`}
onClick={() => !isLoading && setDeleteDialogOpen(true)}
/>

View File

@ -322,7 +322,7 @@ export default function ZoneEditPane({
<Heading as="h3" className="my-2">
{polygon.name.length ? "Edit" : "New"} Zone
</Heading>
<div className="text-sm text-muted-foreground my-2">
<div className="my-2 text-sm text-muted-foreground">
<p>
Zones allow you to define a specific area of the frame so you can
determine whether or not an object is within a particular area.
@ -330,7 +330,7 @@ export default function ZoneEditPane({
</div>
<Separator className="my-3 bg-secondary" />
{polygons && activePolygonIndex !== undefined && (
<div className="flex flex-row my-2 text-sm w-full justify-between">
<div className="my-2 flex w-full flex-row justify-between text-sm">
<div className="my-1 inline-flex">
{polygons[activePolygonIndex].points.length}{" "}
{polygons[activePolygonIndex].points.length > 1 ||
@ -364,7 +364,7 @@ export default function ZoneEditPane({
<FormLabel>Name</FormLabel>
<FormControl>
<Input
className="w-full p-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
placeholder="Enter a name..."
{...field}
/>
@ -377,7 +377,7 @@ export default function ZoneEditPane({
</FormItem>
)}
/>
<Separator className="flex my-2 bg-secondary" />
<Separator className="my-2 flex bg-secondary" />
<FormField
control={form.control}
name="inertia"
@ -386,7 +386,7 @@ export default function ZoneEditPane({
<FormLabel>Inertia</FormLabel>
<FormControl>
<Input
className="w-full p-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
placeholder="3"
{...field}
/>
@ -399,7 +399,7 @@ export default function ZoneEditPane({
</FormItem>
)}
/>
<Separator className="flex my-2 bg-secondary" />
<Separator className="my-2 flex bg-secondary" />
<FormField
control={form.control}
name="loitering_time"
@ -408,7 +408,7 @@ export default function ZoneEditPane({
<FormLabel>Loitering Time</FormLabel>
<FormControl>
<Input
className="w-full p-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
placeholder="0"
{...field}
/>
@ -421,7 +421,7 @@ export default function ZoneEditPane({
</FormItem>
)}
/>
<Separator className="flex my-2 bg-secondary" />
<Separator className="my-2 flex bg-secondary" />
<FormItem>
<FormLabel>Objects</FormLabel>
<FormDescription>
@ -446,7 +446,7 @@ export default function ZoneEditPane({
/>
</FormItem>
<Separator className="flex my-2 bg-secondary" />
<Separator className="my-2 flex bg-secondary" />
<FormField
control={form.control}
@ -585,8 +585,8 @@ export function ZoneObjectSelector({
return (
<>
<div className="h-auto overflow-y-auto overflow-x-hidden">
<div className="flex justify-between items-center my-2.5">
<Label className="text-primary cursor-pointer" htmlFor="allLabels">
<div className="my-2.5 flex items-center justify-between">
<Label className="cursor-pointer text-primary" htmlFor="allLabels">
All Objects
</Label>
<Switch
@ -603,9 +603,9 @@ export function ZoneObjectSelector({
<Separator />
<div className="my-2.5 flex flex-col gap-2.5">
{allLabels.map((item) => (
<div key={item} className="flex justify-between items-center">
<div key={item} className="flex items-center justify-between">
<Label
className="w-full text-primary capitalize cursor-pointer"
className="w-full cursor-pointer capitalize text-primary"
htmlFor={item}
>
{item.replaceAll("_", " ")}

View File

@ -229,16 +229,16 @@ export function EventSegment({
{severityValue === displaySeverityType && (
<HoverCard openDelay={200} closeDelay={100}>
<HoverCardTrigger asChild>
<div className="absolute left-1/2 transform -translate-x-1/2 w-[20px] md:w-[40px] h-[8px] z-10 cursor-pointer">
<div className="flex flex-row justify-center w-[20px] md:w-[40px]">
<div className="absolute left-1/2 z-10 h-[8px] w-[20px] -translate-x-1/2 transform cursor-pointer md:w-[40px]">
<div className="flex w-[20px] flex-row justify-center md:w-[40px]">
<div className="flex justify-center">
<div
className="absolute left-1/2 transform -translate-x-1/2 w-[8px] h-[8px] ml-[2px] z-10 cursor-pointer"
className="absolute left-1/2 z-10 ml-[2px] h-[8px] w-[8px] -translate-x-1/2 transform cursor-pointer"
data-severity={severityValue}
>
<div
key={`${segmentKey}_${index}_primary_data`}
className={`w-full h-[8px] bg-gradient-to-r ${roundBottomPrimary ? "rounded-bl-full rounded-br-full" : ""} ${roundTopPrimary ? "rounded-tl-full rounded-tr-full" : ""} ${severityColors[severityValue]}`}
className={`h-[8px] w-full bg-gradient-to-r ${roundBottomPrimary ? "rounded-bl-full rounded-br-full" : ""} ${roundTopPrimary ? "rounded-tl-full rounded-tr-full" : ""} ${severityColors[severityValue]}`}
></div>
</div>
</div>
@ -247,7 +247,7 @@ export function EventSegment({
</HoverCardTrigger>
<HoverCardPortal>
<HoverCardContent
className="rounded-lg md:rounded-2xl w-[250px] p-2"
className="w-[250px] rounded-lg p-2 md:rounded-2xl"
side="left"
>
<img

View File

@ -204,9 +204,9 @@ export function MotionSegment({
</>
)}
<div className="absolute left-1/2 transform -translate-x-1/2 w-[20px] md:w-[40px] h-[8px] z-10 cursor-pointer">
<div className="flex flex-row justify-center w-[20px] md:w-[40px] pt-[1px] mb-[1px]">
<div className="flex justify-center mb-[1px]">
<div className="absolute left-1/2 z-10 h-[8px] w-[20px] -translate-x-1/2 transform cursor-pointer md:w-[40px]">
<div className="mb-[1px] flex w-[20px] flex-row justify-center pt-[1px] md:w-[40px]">
<div className="mb-[1px] flex justify-center">
<div
key={`${segmentKey}_motion_data_1`}
data-motion-value={secondHalfSegmentWidth}
@ -218,7 +218,7 @@ export function MotionSegment({
</div>
</div>
<div className="flex flex-row justify-center pb-[1px] w-[20px] md:w-[40px]">
<div className="flex w-[20px] flex-row justify-center pb-[1px] md:w-[40px]">
<div className="flex justify-center">
<div
key={`${segmentKey}_motion_data_2`}

View File

@ -316,15 +316,15 @@ export function ReviewTimeline({
return (
<div
ref={timelineRef}
className={`relative h-full overflow-y-auto no-scrollbar select-none bg-secondary ${
className={`no-scrollbar relative h-full select-none overflow-y-auto bg-secondary ${
isDragging && (showHandlebar || showExportHandles)
? "cursor-grabbing"
: "cursor-auto"
}`}
>
<div ref={segmentsRef} className="flex flex-col relative">
<div className="absolute top-0 inset-x-0 z-20 w-full h-[30px] bg-gradient-to-b from-secondary to-transparent pointer-events-none"></div>
<div className="absolute bottom-0 inset-x-0 z-20 w-full h-[30px] bg-gradient-to-t from-secondary to-transparent pointer-events-none"></div>
<div ref={segmentsRef} className="relative flex flex-col">
<div className="pointer-events-none absolute inset-x-0 top-0 z-20 h-[30px] w-full bg-gradient-to-b from-secondary to-transparent"></div>
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-20 h-[30px] w-full bg-gradient-to-t from-secondary to-transparent"></div>
{children}
</div>
{children.length > 0 && (
@ -336,7 +336,7 @@ export function ReviewTimeline({
ref={handlebarRef}
>
<div
className="flex items-center justify-center touch-none select-none"
className="flex touch-none select-none items-center justify-center"
onMouseDown={handleHandlebar}
onTouchStart={handleHandlebar}
>
@ -346,21 +346,21 @@ export function ReviewTimeline({
}`}
>
<div
className={`bg-destructive rounded-full mx-auto ${
className={`mx-auto rounded-full bg-destructive ${
dense
? "w-12 md:w-20"
: segmentDuration < 60
? "w-24"
: "w-20"
} h-5 ${isDraggingHandlebar && isMobile ? "fixed top-[18px] left-1/2 transform -translate-x-1/2 z-20 w-32 h-[30px] bg-destructive/80" : "static"} flex items-center justify-center`}
} h-5 ${isDraggingHandlebar && isMobile ? "fixed left-1/2 top-[18px] z-20 h-[30px] w-32 -translate-x-1/2 transform bg-destructive/80" : "static"} flex items-center justify-center`}
>
<div
ref={handlebarTimeRef}
className={`text-white pointer-events-none ${textSizeClasses("handlebar")} z-10`}
className={`pointer-events-none text-white ${textSizeClasses("handlebar")} z-10`}
></div>
</div>
<div
className={`absolute h-[4px] w-full bg-destructive ${isDraggingHandlebar && isMobile ? "top-1" : "top-1/2 transform -translate-y-1/2"}`}
className={`absolute h-[4px] w-full bg-destructive ${isDraggingHandlebar && isMobile ? "top-1" : "top-1/2 -translate-y-1/2 transform"}`}
></div>
</div>
</div>
@ -374,7 +374,7 @@ export function ReviewTimeline({
ref={exportEndRef}
>
<div
className="flex items-center justify-center touch-none select-none"
className="flex touch-none select-none items-center justify-center"
onMouseDown={handleExportEnd}
onTouchStart={handleExportEnd}
>
@ -384,28 +384,28 @@ export function ReviewTimeline({
}`}
>
<div
className={`bg-selected -mt-4 mx-auto ${
className={`mx-auto -mt-4 bg-selected ${
dense
? "w-12 md:w-20"
: segmentDuration < 60
? "w-24"
: "w-20"
} h-5 ${isDraggingExportEnd && isMobile ? "fixed mt-0 rounded-full top-[18px] left-1/2 transform -translate-x-1/2 z-20 w-32 h-[30px] bg-selected/80" : "rounded-tr-lg rounded-tl-lg static"} flex items-center justify-center`}
} h-5 ${isDraggingExportEnd && isMobile ? "fixed left-1/2 top-[18px] z-20 mt-0 h-[30px] w-32 -translate-x-1/2 transform rounded-full bg-selected/80" : "static rounded-tl-lg rounded-tr-lg"} flex items-center justify-center`}
>
<div
ref={exportEndTimeRef}
className={`text-white pointer-events-none ${isDraggingExportEnd && isMobile ? "mt-0" : ""} ${textSizeClasses("export_end")} z-10`}
className={`pointer-events-none text-white ${isDraggingExportEnd && isMobile ? "mt-0" : ""} ${textSizeClasses("export_end")} z-10`}
></div>
</div>
<div
className={`absolute h-[4px] w-full bg-selected ${isDraggingExportEnd && isMobile ? "top-0" : "top-1/2 transform -translate-y-1/2"}`}
className={`absolute h-[4px] w-full bg-selected ${isDraggingExportEnd && isMobile ? "top-0" : "top-1/2 -translate-y-1/2 transform"}`}
></div>
</div>
</div>
</div>
<div
ref={exportSectionRef}
className="bg-selected/50 absolute w-full"
className="absolute w-full bg-selected/50"
></div>
<div
className={`export-start absolute left-0 top-0 ${isDraggingExportStart && isIOS ? "" : "z-20"} w-full`}
@ -413,7 +413,7 @@ export function ReviewTimeline({
ref={exportStartRef}
>
<div
className="flex items-center justify-center touch-none select-none"
className="flex touch-none select-none items-center justify-center"
onMouseDown={handleExportStart}
onTouchStart={handleExportStart}
>
@ -423,20 +423,20 @@ export function ReviewTimeline({
}`}
>
<div
className={`absolute h-[4px] w-full bg-selected ${isDraggingExportStart && isMobile ? "top-[12px]" : "top-1/2 transform -translate-y-1/2"}`}
className={`absolute h-[4px] w-full bg-selected ${isDraggingExportStart && isMobile ? "top-[12px]" : "top-1/2 -translate-y-1/2 transform"}`}
></div>
<div
className={`bg-selected mt-4 mx-auto ${
className={`mx-auto mt-4 bg-selected ${
dense
? "w-12 md:w-20"
: segmentDuration < 60
? "w-24"
: "w-20"
} h-5 ${isDraggingExportStart && isMobile ? "fixed mt-0 rounded-full top-[4px] left-1/2 transform -translate-x-1/2 z-20 w-32 h-[30px] bg-selected/80" : "rounded-br-lg rounded-bl-lg static"} flex items-center justify-center`}
} h-5 ${isDraggingExportStart && isMobile ? "fixed left-1/2 top-[4px] z-20 mt-0 h-[30px] w-32 -translate-x-1/2 transform rounded-full bg-selected/80" : "static rounded-bl-lg rounded-br-lg"} flex items-center justify-center`}
>
<div
ref={exportStartTimeRef}
className={`text-white pointer-events-none ${isDraggingExportStart && isMobile ? "mt-0" : ""} ${textSizeClasses("export_start")} z-10`}
className={`pointer-events-none text-white ${isDraggingExportStart && isMobile ? "mt-0" : ""} ${textSizeClasses("export_start")} z-10`}
></div>
</div>
</div>

View File

@ -51,7 +51,7 @@ export function SummarySegment({
<React.Fragment key={index}>
{severityValue === displaySeverityType && (
<div
className="flex justify-end cursor-pointer"
className="flex cursor-pointer justify-end"
style={{ height: segmentHeight }}
>
<div

View File

@ -339,12 +339,12 @@ export function SummaryTimeline({
return (
<div
className={`relative h-full overflow-hidden no-scrollbar select-none bg-secondary border-l-[1px] border-neutral-700`}
className={`no-scrollbar relative h-full select-none overflow-hidden border-l-[1px] border-neutral-700 bg-secondary`}
role="scrollbar"
>
<div
ref={summaryTimelineRef}
className="h-full flex flex-col relative z-10"
className="relative z-10 flex h-full flex-col"
onClick={timelineClick}
onTouchEnd={timelineClick}
>
@ -354,7 +354,7 @@ export function SummaryTimeline({
ref={visibleSectionRef}
onMouseDown={handleMouseDown}
onTouchStart={handleMouseDown}
className={`bg-primary/30 z-20 absolute w-full touch-none ${
className={`absolute z-20 w-full touch-none bg-primary/30 ${
isDragging ? "cursor-grabbing" : "cursor-grab"
}`}
></div>

View File

@ -32,7 +32,7 @@ export function MinimapBounds({
<>
{isFirstSegmentInMinimap && (
<div
className="absolute inset-0 -bottom-7 w-full flex items-center justify-center text-primary font-medium z-20 text-center text-[10px] scroll-mt-8 pointer-events-none select-none"
className="pointer-events-none absolute inset-0 -bottom-7 z-20 flex w-full select-none scroll-mt-8 items-center justify-center text-center text-[10px] font-medium text-primary"
ref={firstMinimapSegmentRef}
>
{new Date(alignedMinimapStartTime * 1000).toLocaleTimeString([], {
@ -44,7 +44,7 @@ export function MinimapBounds({
)}
{isLastSegmentInMinimap && (
<div className="absolute inset-0 -top-3 w-full flex items-center justify-center text-primary font-medium z-20 text-center text-[10px] pointer-events-none select-none">
<div className="pointer-events-none absolute inset-0 -top-3 z-20 flex w-full select-none items-center justify-center text-center text-[10px] font-medium text-primary">
{new Date(alignedMinimapEndTime * 1000).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
@ -59,9 +59,9 @@ export function MinimapBounds({
export function Tick({ timestamp, timestampSpread }: TickSegmentProps) {
return (
<div className="absolute">
<div className="flex items-end content-end w-[12px] h-[8px]">
<div className="flex h-[8px] w-[12px] content-end items-end">
<div
className={`pointer-events-none select-none h-0.5 ${
className={`pointer-events-none h-0.5 select-none ${
timestamp.getMinutes() % timestampSpread === 0 &&
timestamp.getSeconds() === 0
? "w-[12px] bg-neutral-600 dark:bg-neutral-500"
@ -84,7 +84,7 @@ export function Timestamp({
segmentKey,
}: TimestampSegmentProps) {
return (
<div className="absolute left-[15px] h-[8px] z-10">
<div className="absolute left-[15px] z-10 h-[8px]">
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && (
<div
key={`${segmentKey}_timestamp`}

View File

@ -127,8 +127,8 @@ function ConfigEditor() {
}
return (
<div className="absolute top-2 bottom-16 right-0 left-0 md:left-2">
<div className="lg:flex justify-between mr-1">
<div className="absolute bottom-16 left-0 right-0 top-2 md:left-2">
<div className="mr-1 justify-between lg:flex">
<Heading as="h2">Config</Heading>
<div>
<Button size="sm" className="mx-1" onClick={() => handleCopyConfig()}>
@ -152,12 +152,12 @@ function ConfigEditor() {
</div>
{error && (
<div className="p-4 overflow-scroll text-danger whitespace-pre-wrap">
<div className="overflow-scroll whitespace-pre-wrap p-4 text-danger">
{error}
</div>
)}
<div ref={configRef} className="h-full mt-2" />
<div ref={configRef} className="mt-2 h-full" />
<Toaster closeButton={true} />
</div>
);

View File

@ -77,7 +77,7 @@ function Exports() {
const [selected, setSelected] = useState<Export>();
return (
<div className="size-full p-2 overflow-hidden flex flex-col gap-2">
<div className="flex size-full flex-col gap-2 overflow-hidden p-2">
<AlertDialog
open={deleteClip != undefined}
onOpenChange={() => setDeleteClip(undefined)}
@ -128,9 +128,9 @@ function Exports() {
</DialogContent>
</Dialog>
<div className="w-full p-2 flex items-center justify-center">
<div className="flex w-full items-center justify-center p-2">
<Input
className="w-full md:w-1/3 bg-muted"
className="w-full bg-muted md:w-1/3"
placeholder="Search"
value={search}
onChange={(e) => setSearch(e.target.value)}
@ -139,7 +139,7 @@ function Exports() {
<div className="w-full overflow-hidden">
{exports && filteredExports && (
<div className="size-full grid gap-2 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 overflow-y-auto">
<div className="grid size-full gap-2 overflow-y-auto sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{Object.values(exports).map((item) => (
<ExportCard
key={item.name}

View File

@ -332,13 +332,13 @@ function Logs() {
const [selectedLog, setSelectedLog] = useState<LogLine>();
return (
<div className="size-full p-2 flex flex-col">
<div className="flex size-full flex-col p-2">
<Toaster position="top-center" closeButton={true} />
<LogInfoDialog logLine={selectedLog} setLogLine={setSelectedLog} />
<div className="flex justify-between items-center">
<div className="flex items-center justify-between">
<ToggleGroup
className="*:px-3 *:py-4 *:rounded-md"
className="*:rounded-md *:px-3 *:py-4"
type="single"
size="sm"
value={logService}
@ -363,12 +363,12 @@ function Logs() {
</ToggleGroup>
<div className="flex items-center gap-2">
<Button
className="flex justify-between items-center gap-2"
className="flex items-center justify-between gap-2"
size="sm"
onClick={handleCopyLogs}
>
<FaCopy className="text-secondary-foreground" />
<div className="hidden md:block text-primary">
<div className="hidden text-primary md:block">
Copy to Clipboard
</div>
</Button>
@ -381,7 +381,7 @@ function Logs() {
{initialScroll && !endVisible && (
<Button
className="absolute bottom-8 left-[50%] -translate-x-[50%] rounded-md text-primary bg-secondary-foreground z-20 p-2"
className="absolute bottom-8 left-[50%] z-20 -translate-x-[50%] rounded-md bg-secondary-foreground p-2 text-primary"
onClick={() =>
contentRef.current?.scrollTo({
top: contentRef.current?.scrollHeight,
@ -393,20 +393,20 @@ function Logs() {
</Button>
)}
<div className="relative size-full flex flex-col my-2 font-mono text-sm sm:p-2 whitespace-pre-wrap bg-background_alt border border-secondary rounded-md overflow-hidden">
<div className="grid grid-cols-5 sm:grid-cols-8 md:grid-cols-12 *:px-2 *:py-3 *:text-sm *:text-primary/40">
<div className="p-1 flex items-center capitalize">Type</div>
<div className="col-span-2 sm:col-span-1 flex items-center">
<div className="font-mono relative my-2 flex size-full flex-col overflow-hidden whitespace-pre-wrap rounded-md border border-secondary bg-background_alt text-sm sm:p-2">
<div className="grid grid-cols-5 *:px-2 *:py-3 *:text-sm *:text-primary/40 sm:grid-cols-8 md:grid-cols-12">
<div className="flex items-center p-1 capitalize">Type</div>
<div className="col-span-2 flex items-center sm:col-span-1">
Timestamp
</div>
<div className="col-span-2 flex items-center">Tag</div>
<div className="col-span-5 sm:col-span-4 md:col-span-8 flex items-center">
<div className="col-span-5 flex items-center sm:col-span-4 md:col-span-8">
Message
</div>
</div>
<div
ref={contentRef}
className="w-full flex flex-col overflow-y-auto no-scrollbar overscroll-contain"
className="no-scrollbar flex w-full flex-col overflow-y-auto overscroll-contain"
>
{logLines.length > 0 &&
[...Array(logRange.end).keys()].map((idx) => {
@ -449,7 +449,7 @@ function Logs() {
{logLines.length > 0 && <div id="page-bottom" ref={endLogRef} />}
</div>
{logLines.length == 0 && (
<ActivityIndicator className="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2" />
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
)}
</div>
</div>
@ -474,25 +474,25 @@ function LogLineData({
<div
ref={startRef}
className={cn(
"w-full py-2 grid grid-cols-5 sm:grid-cols-8 md:grid-cols-12 gap-2 border-secondary border-t cursor-pointer hover:bg-muted",
"grid w-full cursor-pointer grid-cols-5 gap-2 border-t border-secondary py-2 hover:bg-muted sm:grid-cols-8 md:grid-cols-12",
className,
"*:text-sm",
)}
onClick={onSelect}
>
<div className="h-full p-1 flex items-center gap-2">
<div className="flex h-full items-center gap-2 p-1">
<LogChip severity={line.severity} onClickSeverity={onClickSeverity} />
</div>
<div className="h-full col-span-2 sm:col-span-1 flex items-center">
<div className="col-span-2 flex h-full items-center sm:col-span-1">
{line.dateStamp}
</div>
<div className="size-full pr-2 col-span-2 flex items-center">
<div className="w-full overflow-hidden whitespace-nowrap text-ellipsis">
<div className="col-span-2 flex size-full items-center pr-2">
<div className="w-full overflow-hidden text-ellipsis whitespace-nowrap">
{line.section}
</div>
</div>
<div className="size-full pl-2 sm:pl-0 pr-2 col-span-5 sm:col-span-4 md:col-span-8 flex justify-between items-center">
<div className="w-full overflow-hidden whitespace-nowrap text-ellipsis">
<div className="col-span-5 flex size-full items-center justify-between pl-2 pr-2 sm:col-span-4 sm:pl-0 md:col-span-8">
<div className="w-full overflow-hidden text-ellipsis whitespace-nowrap">
{line.content}
</div>
</div>

View File

@ -105,12 +105,12 @@ export default function Settings() {
}, []);
return (
<div className="size-full p-2 flex flex-col">
<div className="w-full h-11 relative flex justify-between items-center">
<div className="flex size-full flex-col p-2">
<div className="relative flex h-11 w-full items-center justify-between">
<ScrollArea className="w-full whitespace-nowrap">
<div ref={tabsRef} className="flex flex-row">
<ToggleGroup
className="*:px-3 *:py-4 *:rounded-md"
className="*:rounded-md *:px-3 *:py-4"
type="single"
size="sm"
value={pageToggle}
@ -123,7 +123,7 @@ export default function Settings() {
{Object.values(settingsViews).map((item) => (
<ToggleGroupItem
key={item}
className={`flex items-center justify-between gap-2 scroll-mx-10 ${page == "general" ? "last:mr-20" : ""} ${pageToggle == item ? "" : "*:text-muted-foreground"}`}
className={`flex scroll-mx-10 items-center justify-between gap-2 ${page == "general" ? "last:mr-20" : ""} ${pageToggle == item ? "" : "*:text-muted-foreground"}`}
value={item}
data-nav-item={item}
aria-label={`Select ${item}`}
@ -138,7 +138,7 @@ export default function Settings() {
{(page == "debug" ||
page == "masks / zones" ||
page == "motion tuner") && (
<div className="flex items-center gap-2 ml-2 flex-shrink-0">
<div className="ml-2 flex flex-shrink-0 items-center gap-2">
{page == "masks / zones" && (
<ZoneMaskFilterButton
selectedZoneMask={filterZoneMask}
@ -153,7 +153,7 @@ export default function Settings() {
</div>
)}
</div>
<div className="mt-2 flex flex-col items-start w-full h-full md:h-dvh md:pb-24">
<div className="mt-2 flex h-full w-full flex-col items-start md:h-dvh md:pb-24">
{page == "general" && <General />}
{page == "debug" && <ObjectSettings selectedCamera={selectedCamera} />}
{page == "masks / zones" && (
@ -216,11 +216,11 @@ function CameraSelectButton({
const trigger = (
<Button
className="flex items-center gap-2 capitalize bg-selected hover:bg-selected"
className="flex items-center gap-2 bg-selected capitalize hover:bg-selected"
size="sm"
>
<FaVideo className="text-background dark:text-primary" />
<div className="hidden md:block text-background dark:text-primary">
<div className="hidden text-background dark:text-primary md:block">
{selectedCamera == undefined
? "No Camera"
: selectedCamera.replaceAll("_", " ")}
@ -237,7 +237,7 @@ function CameraSelectButton({
<DropdownMenuSeparator />
</>
)}
<div className="h-auto max-h-[80dvh] p-4 mb-5 md:mb-1 overflow-y-auto overflow-x-hidden">
<div className="mb-5 h-auto max-h-[80dvh] overflow-y-auto overflow-x-hidden p-4 md:mb-1">
<div className="flex flex-col gap-2.5">
{allCameras.map((item) => (
<FilterSwitch

View File

@ -224,8 +224,8 @@ export default function SubmitPlus() {
);
return (
<div className="size-full flex flex-col">
<div className="w-full h-16 px-2 flex items-center justify-between overflow-x-auto">
<div className="flex size-full flex-col">
<div className="flex h-16 w-full items-center justify-between overflow-x-auto px-2">
<PlusFilterGroup
selectedCameras={selectedCameras}
selectedLabels={selectedLabels}
@ -236,8 +236,8 @@ export default function SubmitPlus() {
/>
<PlusSortSelector selectedSort={sort} setSelectedSort={setSort} />
</div>
<div className="size-full flex flex-1 flex-wrap content-start gap-2 md:gap-4 overflow-y-auto no-scrollbar">
<div className="w-full p-2 grid sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-2">
<div className="no-scrollbar flex size-full flex-1 flex-wrap content-start gap-2 overflow-y-auto md:gap-4">
<div className="grid w-full gap-2 p-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
<Dialog
open={upload != undefined}
onOpenChange={(open) => (!open ? setUpload(undefined) : null)}
@ -286,16 +286,16 @@ export default function SubmitPlus() {
<div
key={event.id}
ref={lastRow ? lastEventRef : null}
className="w-full relative rounded-lg md:rounded-2xl aspect-video flex justify-center items-center bg-black cursor-pointer"
className="relative flex aspect-video w-full cursor-pointer items-center justify-center rounded-lg bg-black md:rounded-2xl"
onClick={() => setUpload(event)}
>
<div className="absolute left-0 top-2 z-40">
<Tooltip>
<div className="flex">
<TooltipTrigger asChild>
<div className="mx-3 pb-1 text-white text-sm">
<div className="mx-3 pb-1 text-sm text-white">
<Chip
className={`flex items-start justify-between space-x-1 bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500 z-0`}
className={`z-0 flex items-start justify-between space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500`}
>
{[event.label].map((object) => {
return getIconForLabel(
@ -317,7 +317,7 @@ export default function SubmitPlus() {
</Tooltip>
</div>
<img
className="aspect-video h-full object-contain rounded-lg md:rounded-2xl"
className="aspect-video h-full rounded-lg object-contain md:rounded-2xl"
src={`${baseUrl}api/events/${event.id}/snapshot.jpg`}
loading="lazy"
/>
@ -392,7 +392,7 @@ function PlusFilterGroup({
const Content = isMobile ? DrawerContent : DropdownMenuContent;
return (
<div className="h-full flex justify-start gap-2 items-center">
<div className="flex h-full items-center justify-start gap-2">
<CamerasFilterButton
allCameras={allCameras}
groups={[]}
@ -417,7 +417,7 @@ function PlusFilterGroup({
<FaList
className={`${selectedLabels == undefined ? "text-secondary-foreground" : "text-selected-foreground"}`}
/>
<div className="hidden md:block text-primary">
<div className="hidden text-primary md:block">
{selectedLabels == undefined
? "All Labels"
: `${selectedLabels.length} Labels`}
@ -450,7 +450,7 @@ function PlusFilterGroup({
<PiSlidersHorizontalFill
className={`${selectedScoreRange == undefined ? "text-secondary-foreground" : "text-selected-foreground"}`}
/>
<div className="hidden md:block text-primary">
<div className="hidden text-primary md:block">
{selectedScoreRange == undefined
? "Score Range"
: `${selectedScoreRange[0] * 100}% - ${selectedScoreRange[1] * 100}%`}
@ -458,7 +458,7 @@ function PlusFilterGroup({
</Button>
</Trigger>
<Content
className={`min-w-80 p-2 flex flex-col justify-center ${isMobile ? "gap-2 *:max-h-[75dvh]" : ""}`}
className={`flex min-w-80 flex-col justify-center p-2 ${isMobile ? "gap-2 *:max-h-[75dvh]" : ""}`}
>
<div className="flex items-center gap-1">
<Input
@ -493,7 +493,7 @@ function PlusFilterGroup({
/>
</div>
<DropdownMenuSeparator />
<div className="p-2 flex justify-evenly items-center">
<div className="flex items-center justify-evenly p-2">
<Button
variant="select"
onClick={() => {
@ -547,7 +547,7 @@ function PlusSortSelector({
const Content = isMobile ? DrawerContent : DropdownMenuContent;
return (
<div className="h-full flex justify-start gap-2 items-center">
<div className="flex h-full items-center justify-start gap-2">
<Menu
open={open}
onOpenChange={(open) => {
@ -572,24 +572,24 @@ function PlusSortSelector({
<Sort
className={`${selectedSort == undefined ? "text-secondary-foreground" : "text-selected-foreground"}`}
/>
<div className="hidden md:block text-primary">
<div className="hidden text-primary md:block">
{selectedSort == undefined ? "Sort" : selectedSort.split("_")[0]}
</div>
</Button>
</Trigger>
<Content
className={`p-2 flex flex-col justify-center gap-2 ${isMobile ? "max-h-[75dvh]" : ""}`}
className={`flex flex-col justify-center gap-2 p-2 ${isMobile ? "max-h-[75dvh]" : ""}`}
>
<RadioGroup
className={`flex flex-col gap-4 ${isMobile ? "mt-4" : ""}`}
onValueChange={(value) => setCurrentSort(value)}
>
<div className="w-full flex items-center gap-2">
<div className="flex w-full items-center gap-2">
<RadioGroupItem
className={
currentSort == "date"
? "from-selected/50 to-selected/90 text-selected bg-selected"
: "from-secondary/50 to-secondary/90 text-secondary bg-secondary"
? "bg-selected from-selected/50 to-selected/90 text-selected"
: "bg-secondary from-secondary/50 to-secondary/90 text-secondary"
}
id="date"
value="date"
@ -616,12 +616,12 @@ function PlusSortSelector({
<div className="size-5" />
)}
</div>
<div className="w-full flex items-center gap-2">
<div className="flex w-full items-center gap-2">
<RadioGroupItem
className={
currentSort == "score"
? "from-selected/50 to-selected/90 text-selected bg-selected"
: "from-secondary/50 to-secondary/90 text-secondary bg-secondary"
? "bg-selected from-selected/50 to-selected/90 text-selected"
: "bg-secondary from-secondary/50 to-secondary/90 text-secondary"
}
id="score"
value="score"
@ -650,7 +650,7 @@ function PlusSortSelector({
</div>
</RadioGroup>
<DropdownMenuSeparator />
<div className="p-2 flex justify-evenly items-center">
<div className="flex items-center justify-evenly p-2">
<Button
variant="select"
onClick={() => {

View File

@ -41,13 +41,13 @@ function System() {
});
return (
<div className="size-full p-2 flex flex-col">
<div className="w-full h-11 relative flex justify-between items-center">
<div className="flex size-full flex-col p-2">
<div className="relative flex h-11 w-full items-center justify-between">
{isMobile && (
<Logo className="absolute inset-x-1/2 -translate-x-1/2 h-8" />
<Logo className="absolute inset-x-1/2 h-8 -translate-x-1/2" />
)}
<ToggleGroup
className="*:px-3 *:py-4 *:rounded-md"
className="*:rounded-md *:px-3 *:py-4"
type="single"
size="sm"
value={pageToggle}
@ -72,18 +72,18 @@ function System() {
))}
</ToggleGroup>
<div className="h-full flex items-center">
<div className="flex h-full items-center">
{lastUpdated && (
<div className="h-full text-muted-foreground text-sm content-center">
<div className="h-full content-center text-sm text-muted-foreground">
Last refreshed: <TimeAgo time={lastUpdated * 1000} dense />
</div>
)}
</div>
</div>
<div className="mt-2 flex items-end gap-2">
<div className="h-full font-medium content-center">System</div>
<div className="h-full content-center font-medium">System</div>
{statsSnapshot && (
<div className="h-full text-muted-foreground text-sm content-center">
<div className="h-full content-center text-sm text-muted-foreground">
{statsSnapshot.service.version}
</div>
)}

View File

@ -55,9 +55,9 @@ const colors = [
function ColorSwatch({ name, value }: { name: string; value: string }) {
return (
<div className="flex items-center mb-2">
<div className="mb-2 flex items-center">
<div
className="w-10 h-10 mr-2 border border-gray-300"
className="mr-2 h-10 w-10 border border-gray-300"
style={{ backgroundColor: value }}
></div>
<span>{name}</span>
@ -212,9 +212,9 @@ function UIPlayground() {
return (
<>
<div className="w-full h-full">
<div className="h-full w-full">
<div className="flex h-full">
<div className="flex-1 content-start gap-2 overflow-y-auto no-scrollbar mt-4 mr-5">
<div className="no-scrollbar mr-5 mt-4 flex-1 content-start gap-2 overflow-y-auto">
<Heading as="h2">UI Playground</Heading>
<IconPicker
@ -294,7 +294,7 @@ function UIPlayground() {
</SelectContent>
</Select>
</div>
<div className="flex p-2 justify-start items-center">
<div className="flex items-center justify-start p-2">
<Switch
id="exporthandles"
checked={showExportHandles}
@ -326,7 +326,7 @@ function UIPlayground() {
Export
</Button>
</div>
<div className="w-[40px] my-4">
<div className="my-4 w-[40px]">
<CameraActivityIndicator />
</div>
<p>
@ -378,7 +378,7 @@ function UIPlayground() {
</div>
</div>
<div className="w-[55px] md:w-[100px] overflow-y-auto no-scrollbar">
<div className="no-scrollbar w-[55px] overflow-y-auto md:w-[100px]">
{!isEventsReviewTimeline && (
<MotionReviewTimeline
segmentDuration={zoomSettings.segmentDuration} // seconds per segment

View File

@ -26,28 +26,28 @@ import { TimeRange, Timeline } from "@/types/timeline";
export function getTimelineIcon(timelineItem: Timeline) {
switch (timelineItem.class_type) {
case "visible":
return <LuPlay className="w-4 mr-1" />;
return <LuPlay className="mr-1 w-4" />;
case "gone":
return <IoMdExit className="w-4 mr-1" />;
return <IoMdExit className="mr-1 w-4" />;
case "active":
return <LuPlayCircle className="w-4 mr-1" />;
return <LuPlayCircle className="mr-1 w-4" />;
case "stationary":
return <LuCircle className="w-4 mr-1" />;
return <LuCircle className="mr-1 w-4" />;
case "entered_zone":
return <MdOutlineLocationOn className="w-4 mr-1" />;
return <MdOutlineLocationOn className="mr-1 w-4" />;
case "attribute":
switch (timelineItem.data.attribute) {
case "face":
return <MdFaceUnlock className="w-4 mr-1" />;
return <MdFaceUnlock className="mr-1 w-4" />;
case "license_plate":
return <MdOutlinePictureInPictureAlt className="w-4 mr-1" />;
return <MdOutlinePictureInPictureAlt className="mr-1 w-4" />;
default:
return <LuTruck className="w-4 mr-1" />;
return <LuTruck className="mr-1 w-4" />;
}
case "heard":
return <LuEar className="w-4 mr-1" />;
return <LuEar className="mr-1 w-4" />;
case "external":
return <LuCircleDot className="w-4 mr-1" />;
return <LuCircleDot className="mr-1 w-4" />;
}
}
@ -59,21 +59,21 @@ export function getTimelineIcon(timelineItem: Timeline) {
export function getTimelineDetectionIcon(timelineItem: Timeline) {
switch (timelineItem.data.label) {
case "bicycle":
return <FaBicycle className="w-4 mr-1" />;
return <FaBicycle className="mr-1 w-4" />;
case "car":
return <LuCar className="w-4 mr-1" />;
return <LuCar className="mr-1 w-4" />;
case "cat":
return <LuCat className="w-4 mr-1" />;
return <LuCat className="mr-1 w-4" />;
case "deer":
return <GiDeer className="w-4 mr-1" />;
return <GiDeer className="mr-1 w-4" />;
case "dog":
return <LuDog className="w-4 mr-1" />;
return <LuDog className="mr-1 w-4" />;
case "package":
return <LuPackage className="w-4 mr-1" />;
return <LuPackage className="mr-1 w-4" />;
case "person":
return <LuPersonStanding className="w-4 mr-1" />;
return <LuPersonStanding className="mr-1 w-4" />;
default:
return <LuCamera className="w-4 mr-1" />;
return <LuCamera className="mr-1 w-4" />;
}
}

View File

@ -252,14 +252,14 @@ export default function EventView({
}
return (
<div className="py-2 flex flex-col size-full">
<div className="flex size-full flex-col py-2">
<Toaster closeButton={true} />
<div className="h-11 mb-2 pl-3 pr-2 relative flex justify-between items-center">
<div className="relative mb-2 flex h-11 items-center justify-between pl-3 pr-2">
{isMobile && (
<Logo className="absolute inset-x-1/2 -translate-x-1/2 h-8" />
<Logo className="absolute inset-x-1/2 h-8 -translate-x-1/2" />
)}
<ToggleGroup
className="*:px-3 *:py-4 *:rounded-md"
className="*:rounded-md *:px-3 *:py-4"
type="single"
size="sm"
value={severityToggle}
@ -272,7 +272,7 @@ export default function EventView({
value="alert"
aria-label="Select alerts"
>
<MdCircle className="size-2 md:mr-[10px] text-severity_alert" />
<MdCircle className="size-2 text-severity_alert md:mr-[10px]" />
<div className="hidden md:block">
Alerts{`${reviewCounts.alert > -1 ? reviewCounts.alert : ""}`}
</div>
@ -282,14 +282,14 @@ export default function EventView({
value="detection"
aria-label="Select detections"
>
<MdCircle className="size-2 md:mr-[10px] text-severity_detection" />
<MdCircle className="size-2 text-severity_detection md:mr-[10px]" />
<div className="hidden md:block">
Detections
{`${reviewCounts.detection > -1 ? reviewCounts.detection : ""}`}
</div>
</ToggleGroupItem>
<ToggleGroupItem
className={`px-3 py-4 rounded-lg ${
className={`rounded-lg px-3 py-4 ${
severityToggle == "significant_motion"
? ""
: "text-muted-foreground"
@ -297,7 +297,7 @@ export default function EventView({
value="significant_motion"
aria-label="Select motion"
>
<MdCircle className="size-2 md:mr-[10px] text-severity_significant_motion" />
<MdCircle className="size-2 text-severity_significant_motion md:mr-[10px]" />
<div className="hidden md:block">Motion</div>
</ToggleGroupItem>
</ToggleGroup>
@ -576,11 +576,11 @@ function DetectionReview({
<>
<div
ref={contentRef}
className="flex flex-1 flex-wrap content-start gap-2 md:gap-4 overflow-y-auto no-scrollbar"
className="no-scrollbar flex flex-1 flex-wrap content-start gap-2 overflow-y-auto md:gap-4"
>
{filter?.before == undefined && (
<NewReviewData
className="absolute left-1/2 -translate-x-1/2 z-50 pointer-events-none"
className="pointer-events-none absolute left-1/2 z-50 -translate-x-1/2"
contentRef={contentRef}
reviewItems={currentItems}
itemsToReview={loading ? 0 : itemsToReview}
@ -589,20 +589,20 @@ function DetectionReview({
)}
{!currentItems && (
<div className="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2">
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">
<ActivityIndicator />
</div>
)}
{!loading && currentItems?.length === 0 && (
<div className="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 flex flex-col justify-center items-center text-center">
<div className="absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 flex-col items-center justify-center text-center">
<LuFolderCheck className="size-16" />
There are no {severity.replace(/_/g, " ")}s to review
</div>
)}
<div
className="w-full mx-2 px-1 grid sm:grid-cols-2 md:grid-cols-3 3xl:grid-cols-4 gap-2 md:gap-4"
className="mx-2 grid w-full gap-2 px-1 sm:grid-cols-2 md:grid-cols-3 md:gap-4 3xl:grid-cols-4"
ref={contentRef}
>
{!loading && currentItems
@ -620,7 +620,7 @@ function DetectionReview({
}
className="review-item relative rounded-lg"
>
<div className="aspect-video rounded-lg overflow-hidden">
<div className="aspect-video overflow-hidden rounded-lg">
<PreviewThumbnailPlayer
review={value}
allPreviews={relevantPreviews}
@ -632,7 +632,7 @@ function DetectionReview({
/>
</div>
<div
className={`review-item-ring pointer-events-none z-10 absolute rounded-lg inset-0 size-full -outline-offset-[2.8px] outline outline-[3px] ${selected ? `outline-severity_${value.severity} shadow-severity_${value.severity}` : "outline-transparent duration-500"}`}
className={`review-item-ring pointer-events-none absolute inset-0 z-10 size-full rounded-lg outline outline-[3px] -outline-offset-[2.8px] ${selected ? `outline-severity_${value.severity} shadow-severity_${value.severity}` : "outline-transparent duration-500"}`}
/>
</div>
);
@ -641,12 +641,12 @@ function DetectionReview({
Array(itemsToReview)
.fill(0)
.map((_, idx) => (
<Skeleton key={idx} className="size-full aspect-video" />
<Skeleton key={idx} className="aspect-video size-full" />
))}
{!loading &&
(currentItems?.length ?? 0) > 0 &&
(itemsToReview ?? 0) > 0 && (
<div className="col-span-full flex justify-center items-center">
<div className="col-span-full flex items-center justify-center">
<Button
className="text-white"
variant="select"
@ -660,8 +660,8 @@ function DetectionReview({
)}
</div>
</div>
<div className="w-[65px] md:w-[110px] flex flex-row">
<div className="w-[55px] md:w-[100px] overflow-y-auto no-scrollbar">
<div className="flex w-[65px] flex-row md:w-[110px]">
<div className="no-scrollbar w-[55px] overflow-y-auto md:w-[100px]">
{loading ? (
<Skeleton className="size-full" />
) : (
@ -919,10 +919,10 @@ function MotionReview({
return (
<>
<div className="flex flex-1 flex-wrap content-start gap-2 md:gap-4 overflow-y-auto no-scrollbar">
<div className="no-scrollbar flex flex-1 flex-wrap content-start gap-2 overflow-y-auto md:gap-4">
<div
ref={contentRef}
className="w-full mx-2 px-1 grid sm:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4 gap-2 md:gap-4 overflow-auto no-scrollbar"
className="no-scrollbar mx-2 grid w-full gap-2 overflow-auto px-1 sm:grid-cols-2 md:gap-4 xl:grid-cols-3 3xl:grid-cols-4"
>
{reviewCameras.map((camera) => {
let grow;
@ -965,12 +965,12 @@ function MotionReview({
}
/>
<div
className={`review-item-ring pointer-events-none z-20 absolute rounded-lg inset-0 size-full -outline-offset-[2.8px] outline outline-[3px] ${detectionType ? `outline-severity_${detectionType} shadow-severity_${detectionType}` : "outline-transparent duration-500"}`}
className={`review-item-ring pointer-events-none absolute inset-0 z-20 size-full rounded-lg outline outline-[3px] -outline-offset-[2.8px] ${detectionType ? `outline-severity_${detectionType} shadow-severity_${detectionType}` : "outline-transparent duration-500"}`}
/>
</>
) : (
<Skeleton
className={`rounded-lg md:rounded-2xl size-full ${spans} ${grow}`}
className={`size-full rounded-lg md:rounded-2xl ${spans} ${grow}`}
/>
)}
</div>
@ -978,7 +978,7 @@ function MotionReview({
})}
</div>
</div>
<div className="w-[55px] md:w-[100px] overflow-y-auto no-scrollbar">
<div className="no-scrollbar w-[55px] overflow-y-auto md:w-[100px]">
{motionData ? (
<MotionReviewTimeline
segmentDuration={segmentDuration}

View File

@ -357,11 +357,11 @@ export function RecordingView({
}, [previewRowRef.current?.scrollWidth, previewRowRef.current?.scrollHeight]);
return (
<div ref={contentRef} className="size-full pt-2 flex flex-col">
<div ref={contentRef} className="flex size-full flex-col pt-2">
<Toaster closeButton={true} />
<div className="w-full h-11 mb-2 px-2 relative flex items-center justify-between">
<div className="relative mb-2 flex h-11 w-full items-center justify-between px-2">
{isMobile && (
<Logo className="absolute inset-x-1/2 -translate-x-1/2 h-8" />
<Logo className="absolute inset-x-1/2 h-8 -translate-x-1/2" />
)}
<div className={cn("flex items-center gap-2")}>
<Button
@ -422,7 +422,7 @@ export function RecordingView({
)}
{isDesktop ? (
<ToggleGroup
className="*:px-3 *:py-4 *:rounded-md"
className="*:rounded-md *:px-3 *:py-4"
type="single"
size="sm"
value={timelineType}
@ -469,8 +469,8 @@ export function RecordingView({
<div
ref={mainLayoutRef}
className={cn(
"h-full flex justify-center overflow-hidden",
isDesktop ? "" : "flex-col landscape:flex-row gap-2",
"flex h-full justify-center overflow-hidden",
isDesktop ? "" : "flex-col gap-2 landscape:flex-row",
)}
>
<div
@ -479,7 +479,7 @@ export function RecordingView({
>
<div
className={cn(
"size-full flex items-center",
"flex size-full items-center",
mainCameraAspect == "tall"
? "flex-row justify-evenly"
: "flex-col justify-center gap-2",
@ -491,7 +491,7 @@ export function RecordingView({
"relative",
isDesktop
? cn(
"px-4 flex justify-center",
"flex justify-center px-4",
mainCameraAspect == "tall"
? "h-[50%] md:h-[60%] lg:h-[75%] xl:h-[90%]"
: mainCameraAspect == "wide"
@ -499,10 +499,10 @@ export function RecordingView({
: "",
)
: cn(
"portrait:w-full pt-2",
"pt-2 portrait:w-full",
mainCameraAspect == "wide"
? "landscape:w-full aspect-wide"
: "landscape:h-[94%] aspect-video",
? "aspect-wide landscape:w-full"
: "aspect-video landscape:h-[94%]",
),
)}
style={{
@ -545,8 +545,8 @@ export function RecordingView({
"flex gap-2 overflow-auto",
mainCameraAspect == "tall"
? "h-full w-48 flex-col"
: `w-full h-28`,
previewRowOverflows ? "" : "justify-center items-center",
: `h-28 w-full`,
previewRowOverflows ? "" : "items-center justify-center",
)}
>
<div className="w-2" />
@ -660,12 +660,12 @@ function Timeline({
<div
className={`${
isDesktop
? `${timelineType == "timeline" ? "w-[100px]" : "w-60"} overflow-y-auto no-scrollbar`
: "portrait:flex-grow landscape:w-[20%] overflow-hidden"
? `${timelineType == "timeline" ? "w-[100px]" : "w-60"} no-scrollbar overflow-y-auto`
: "overflow-hidden portrait:flex-grow landscape:w-[20%]"
} relative`}
>
<div className="absolute top-0 inset-x-0 z-20 w-full h-[30px] bg-gradient-to-b from-secondary to-transparent pointer-events-none"></div>
<div className="absolute bottom-0 inset-x-0 z-20 w-full h-[30px] bg-gradient-to-t from-secondary to-transparent pointer-events-none"></div>
<div className="pointer-events-none absolute inset-x-0 top-0 z-20 h-[30px] w-full bg-gradient-to-b from-secondary to-transparent"></div>
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-20 h-[30px] w-full bg-gradient-to-t from-secondary to-transparent"></div>
{timelineType == "timeline" ? (
motionData ? (
<MotionReviewTimeline
@ -693,7 +693,7 @@ function Timeline({
)
) : (
<div
className={`h-full grid grid-cols-1 gap-4 overflow-auto p-4 bg-secondary ${isDesktop ? "" : "sm:grid-cols-2"}`}
className={`grid h-full grid-cols-1 gap-4 overflow-auto bg-secondary p-4 ${isDesktop ? "" : "sm:grid-cols-2"}`}
>
{mainCameraReviewItems.map((review) => {
if (review.severity == "significant_motion") {

View File

@ -325,7 +325,7 @@ export default function DraggableGridLayout({
<>
<Toaster position="top-center" closeButton={true} />
{!isGridLayoutLoaded || !currentGridLayout ? (
<div className="mt-2 px-2 grid grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4 gap-2 md:gap-4">
<div className="mt-2 grid grid-cols-2 gap-2 px-2 md:gap-4 xl:grid-cols-3 3xl:grid-cols-4">
{includeBirdseye && birdseyeConfig?.enabled && (
<Skeleton className="size-full rounded-lg md:rounded-2xl" />
)}
@ -340,7 +340,7 @@ export default function DraggableGridLayout({
</div>
) : (
<div
className="my-2 px-2 pb-8 no-scrollbar overflow-x-hidden"
className="no-scrollbar my-2 overflow-x-hidden px-2 pb-8"
ref={gridContainerRef}
>
<EditGroupDialog
@ -373,7 +373,7 @@ export default function DraggableGridLayout({
key="birdseye"
className={cn(
isEditMode &&
"outline outline-2 hover:outline-4 outline-muted-foreground hover:cursor-grab active:cursor-grabbing",
"outline outline-2 outline-muted-foreground hover:cursor-grab hover:outline-4 active:cursor-grabbing",
)}
birdseyeConfig={birdseyeConfig}
liveMode={birdseyeConfig.restream ? "mse" : "jsmpeg"}
@ -397,10 +397,10 @@ export default function DraggableGridLayout({
key={camera.name}
cameraRef={cameraRef}
className={cn(
"rounded-lg md:rounded-2xl bg-black",
"rounded-lg bg-black md:rounded-2xl",
grow,
isEditMode &&
"outline-2 hover:outline-4 outline-muted-foreground hover:cursor-grab active:cursor-grabbing",
"outline-2 outline-muted-foreground hover:cursor-grab hover:outline-4 active:cursor-grabbing",
)}
windowVisible={
windowVisible && visibleCameras.includes(camera.name)
@ -429,7 +429,7 @@ export default function DraggableGridLayout({
<Tooltip>
<TooltipTrigger asChild>
<div
className="rounded-lg text-secondary-foreground bg-secondary hover:bg-muted cursor-pointer opacity-60 hover:opacity-100 transition-all duration-300"
className="cursor-pointer rounded-lg bg-secondary text-secondary-foreground opacity-60 transition-all duration-300 hover:bg-muted hover:opacity-100"
onClick={() =>
setIsEditMode((prevIsEditMode) => !prevIsEditMode)
}
@ -450,7 +450,7 @@ export default function DraggableGridLayout({
<Tooltip>
<TooltipTrigger asChild>
<div
className="rounded-lg text-secondary-foreground bg-secondary hover:bg-muted cursor-pointer opacity-60 hover:opacity-100 transition-all duration-300"
className="cursor-pointer rounded-lg bg-secondary text-secondary-foreground opacity-60 transition-all duration-300 hover:bg-muted hover:opacity-100"
onClick={() =>
setEditGroup((prevEditGroup) => !prevEditGroup)
}
@ -465,7 +465,7 @@ export default function DraggableGridLayout({
<Tooltip>
<TooltipTrigger asChild>
<div
className="rounded-lg text-secondary-foreground bg-secondary hover:bg-muted cursor-pointer opacity-60 hover:opacity-100 transition-all duration-300"
className="cursor-pointer rounded-lg bg-secondary text-secondary-foreground opacity-60 transition-all duration-300 hover:bg-muted hover:opacity-100"
onClick={toggleFullscreen}
>
{fullscreen ? (
@ -492,10 +492,10 @@ export default function DraggableGridLayout({
function CornerCircles() {
return (
<>
<div className="absolute top-[-4px] left-[-4px] z-50 size-3 p-2 rounded-full bg-primary-variant outline-2 outline-muted text-background pointer-events-none" />
<div className="absolute top-[-4px] right-[-4px] z-50 size-3 p-2 rounded-full bg-primary-variant outline-2 outline-muted text-background pointer-events-none" />
<div className="absolute bottom-[-4px] right-[-4px] z-50 size-3 p-2 rounded-full bg-primary-variant outline-2 outline-muted text-background pointer-events-none" />
<div className="absolute bottom-[-4px] left-[-4px] z-50 size-3 p-2 rounded-full bg-primary-variant outline-2 outline-muted text-background pointer-events-none" />
<div className="pointer-events-none absolute left-[-4px] top-[-4px] z-50 size-3 rounded-full bg-primary-variant p-2 text-background outline-2 outline-muted" />
<div className="pointer-events-none absolute right-[-4px] top-[-4px] z-50 size-3 rounded-full bg-primary-variant p-2 text-background outline-2 outline-muted" />
<div className="pointer-events-none absolute bottom-[-4px] right-[-4px] z-50 size-3 rounded-full bg-primary-variant p-2 text-background outline-2 outline-muted" />
<div className="pointer-events-none absolute bottom-[-4px] left-[-4px] z-50 size-3 rounded-full bg-primary-variant p-2 text-background outline-2 outline-muted" />
</>
);
}

View File

@ -115,20 +115,20 @@ export default function LiveBirdseyeView() {
ref={mainRef}
className={
fullscreen
? `fixed inset-0 bg-black z-30`
: `size-full p-2 flex flex-col ${isMobile ? "landscape:flex-row" : ""}`
? `fixed inset-0 z-30 bg-black`
: `flex size-full flex-col p-2 ${isMobile ? "landscape:flex-row" : ""}`
}
>
<div
className={
fullscreen
? `absolute right-32 top-1 z-40 ${isMobile ? "landscape:left-2 landscape:right-auto landscape:bottom-1 landscape:top-auto" : ""}`
: `w-full h-12 flex flex-row items-center justify-between ${isMobile ? "landscape:w-min landscape:h-full landscape:flex-col" : ""}`
? `absolute right-32 top-1 z-40 ${isMobile ? "landscape:bottom-1 landscape:left-2 landscape:right-auto landscape:top-auto" : ""}`
: `flex h-12 w-full flex-row items-center justify-between ${isMobile ? "landscape:h-full landscape:w-min landscape:flex-col" : ""}`
}
>
{!fullscreen ? (
<Button
className={`rounded-lg flex items-center gap-2 ${isMobile ? "ml-2" : "ml-0"}`}
className={`flex items-center gap-2 rounded-lg ${isMobile ? "ml-2" : "ml-0"}`}
size={isMobile ? "icon" : "sm"}
onClick={() => navigate(-1)}
>
@ -140,7 +140,7 @@ export default function LiveBirdseyeView() {
)}
<TooltipProvider>
<div
className={`flex flex-row items-center gap-2 mr-1 *:rounded-lg ${isMobile ? "landscape:flex-col" : ""}`}
className={`mr-1 flex flex-row items-center gap-2 *:rounded-lg ${isMobile ? "landscape:flex-col" : ""}`}
>
<CameraFeatureToggle
className="p-2 md:p-0"

View File

@ -222,15 +222,15 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
ref={mainRef}
className={
fullscreen
? `fixed inset-0 bg-black z-30`
: `size-full p-2 flex flex-col ${isMobile ? "landscape:flex-row landscape:gap-1" : ""}`
? `fixed inset-0 z-30 bg-black`
: `flex size-full flex-col p-2 ${isMobile ? "landscape:flex-row landscape:gap-1" : ""}`
}
>
<div
className={
fullscreen
? `absolute right-32 top-1 z-40 ${isMobile ? "landscape:left-2 landscape:right-auto landscape:bottom-1 landscape:top-auto" : ""}`
: `w-full h-12 flex flex-row items-center justify-between ${isMobile ? "landscape:w-12 landscape:h-full landscape:flex-col" : ""}`
? `absolute right-32 top-1 z-40 ${isMobile ? "landscape:bottom-1 landscape:left-2 landscape:right-auto landscape:top-auto" : ""}`
: `flex h-12 w-full flex-row items-center justify-between ${isMobile ? "landscape:h-full landscape:w-12 landscape:flex-col" : ""}`
}
>
{!fullscreen ? (
@ -344,7 +344,7 @@ export default function LiveCameraView({ camera }: LiveCameraViewProps) {
}}
>
<div
className={`flex flex-col justify-center items-center ${growClassName}`}
className={`flex flex-col items-center justify-center ${growClassName}`}
ref={clickOverlayRef}
onClick={handleOverlayClick}
style={{
@ -435,7 +435,7 @@ function PtzControlPanel({
);
return (
<div className="absolute inset-x-2 md:left-[50%] md:-translate-x-[50%] bottom-[10%] flex flex-wrap md:flex-nowrap justify-center items-center gap-1">
<div className="absolute inset-x-2 bottom-[10%] flex flex-wrap items-center justify-center gap-1 md:left-[50%] md:-translate-x-[50%] md:flex-nowrap">
{ptz?.features?.includes("pt") && (
<>
<Button
@ -637,7 +637,7 @@ function FrigateCameraFeatures({
title={`${camera} Settings`}
/>
</DrawerTrigger>
<DrawerContent className="px-2 py-4 flex flex-col gap-3 rounded-2xl">
<DrawerContent className="flex flex-col gap-3 rounded-2xl px-2 py-4">
<FilterSwitch
label="Object Detection"
isChecked={detectState == "ON"}

View File

@ -155,10 +155,10 @@ export default function LiveDashboardView({
const birdseyeConfig = useMemo(() => config?.birdseye, [config]);
return (
<div className="size-full p-2 overflow-y-auto" ref={containerRef}>
<div className="size-full overflow-y-auto p-2" ref={containerRef}>
{isMobile && (
<div className="h-11 relative flex items-center justify-between">
<Logo className="absolute inset-x-1/2 -translate-x-1/2 h-8" />
<div className="relative flex h-11 items-center justify-between">
<Logo className="absolute inset-x-1/2 h-8 -translate-x-1/2" />
<div className="max-w-[45%]">
<CameraGroupSelector />
</div>
@ -167,7 +167,7 @@ export default function LiveDashboardView({
<Button
className={`p-1 ${
mobileLayout == "grid"
? "bg-blue-900 focus:bg-blue-900 bg-opacity-60 focus:bg-opacity-60"
? "bg-blue-900 bg-opacity-60 focus:bg-blue-900 focus:bg-opacity-60"
: "bg-secondary"
}`}
size="xs"
@ -178,7 +178,7 @@ export default function LiveDashboardView({
<Button
className={`p-1 ${
mobileLayout == "list"
? "bg-blue-900 focus:bg-blue-900 bg-opacity-60 focus:bg-opacity-60"
? "bg-blue-900 bg-opacity-60 focus:bg-blue-900 focus:bg-opacity-60"
: "bg-secondary"
}`}
size="xs"
@ -194,8 +194,8 @@ export default function LiveDashboardView({
className={cn(
"p-1",
isEditMode
? "text-primary bg-selected"
: "text-secondary-foreground bg-secondary",
? "bg-selected text-primary"
: "bg-secondary text-secondary-foreground",
)}
size="xs"
onClick={() =>
@ -212,7 +212,7 @@ export default function LiveDashboardView({
{events && events.length > 0 && (
<ScrollArea>
<TooltipProvider>
<div className="px-1 flex gap-2 items-center">
<div className="flex items-center gap-2 px-1">
{events.map((event) => {
return <AnimatedEventCard key={event.id} event={event} />;
})}
@ -224,7 +224,7 @@ export default function LiveDashboardView({
{!cameraGroup || cameraGroup == "default" || isMobileOnly ? (
<div
className={`mt-2 px-2 grid ${mobileLayout == "grid" ? "grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4" : ""} gap-2 md:gap-4`}
className={`mt-2 grid px-2 ${mobileLayout == "grid" ? "grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4" : ""} gap-2 md:gap-4`}
>
{includeBirdseye && birdseyeConfig?.enabled && (
<BirdseyeLivePlayer
@ -247,7 +247,7 @@ export default function LiveDashboardView({
<LivePlayer
cameraRef={cameraRef}
key={camera.name}
className={`${grow} rounded-lg md:rounded-2xl bg-black`}
className={`${grow} rounded-lg bg-black md:rounded-2xl`}
windowVisible={
windowVisible && visibleCameras.includes(camera.name)
}

View File

@ -204,11 +204,11 @@ export default function CameraMetrics({
}, [statsHistory]);
return (
<div className="size-full mt-4 flex flex-col gap-3 overflow-y-auto">
<div className="text-muted-foreground text-sm font-medium">Overview</div>
<div className="mt-4 flex size-full flex-col gap-3 overflow-y-auto">
<div className="text-sm font-medium text-muted-foreground">Overview</div>
<div className="grid grid-cols-1 md:grid-cols-3">
{statsHistory.length != 0 ? (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">Frames / Detections</div>
<CameraLineGraph
graphId="overall-stats"
@ -219,21 +219,21 @@ export default function CameraMetrics({
/>
</div>
) : (
<Skeleton className="w-full rounded-lg md:rounded-2xl h-32" />
<Skeleton className="h-32 w-full rounded-lg md:rounded-2xl" />
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
{config &&
Object.values(config.cameras).map((camera) => {
if (camera.enabled) {
return (
<div className="w-full flex flex-col gap-3">
<div className="capitalize text-muted-foreground text-sm font-medium">
<div className="flex w-full flex-col gap-3">
<div className="text-sm font-medium capitalize text-muted-foreground">
{camera.name.replaceAll("_", " ")}
</div>
<div key={camera.name} className="grid sm:grid-cols-2 gap-2">
<div key={camera.name} className="grid gap-2 sm:grid-cols-2">
{Object.keys(cameraCpuSeries).includes(camera.name) ? (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">CPU</div>
<CameraLineGraph
graphId={`${camera.name}-cpu`}
@ -246,10 +246,10 @@ export default function CameraMetrics({
/>
</div>
) : (
<Skeleton className="size-full aspect-video" />
<Skeleton className="aspect-video size-full" />
)}
{Object.keys(cameraFpsSeries).includes(camera.name) ? (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">Frames / Detections</div>
<CameraLineGraph
graphId={`${camera.name}-dps`}
@ -262,7 +262,7 @@ export default function CameraMetrics({
/>
</div>
) : (
<Skeleton className="size-full aspect-video" />
<Skeleton className="aspect-video size-full" />
)}
</div>
</div>

View File

@ -344,15 +344,15 @@ export default function GeneralMetrics({
<>
<VainfoDialog showVainfo={showVainfo} setShowVainfo={setShowVainfo} />
<div className="size-full mt-4 flex flex-col overflow-y-auto">
<div className="text-muted-foreground text-sm font-medium">
<div className="mt-4 flex size-full flex-col overflow-y-auto">
<div className="text-sm font-medium text-muted-foreground">
Detectors
</div>
<div
className={`w-full mt-4 grid grid-cols-1 gap-2 ${detTempSeries == undefined ? "sm:grid-cols-3" : "sm:grid-cols-4"}`}
className={`mt-4 grid w-full grid-cols-1 gap-2 ${detTempSeries == undefined ? "sm:grid-cols-3" : "sm:grid-cols-4"}`}
>
{statsHistory.length != 0 ? (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">Detector Inference Speed</div>
{detInferenceTimeSeries.map((series) => (
<ThresholdBarGraph
@ -367,12 +367,12 @@ export default function GeneralMetrics({
))}
</div>
) : (
<Skeleton className="w-full rounded-lg md:rounded-2xl aspect-video" />
<Skeleton className="aspect-video w-full rounded-lg md:rounded-2xl" />
)}
{statsHistory.length != 0 ? (
<>
{detTempSeries && (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">Detector Temperature</div>
{detTempSeries.map((series) => (
<ThresholdBarGraph
@ -389,10 +389,10 @@ export default function GeneralMetrics({
)}
</>
) : (
<Skeleton className="w-full aspect-video" />
<Skeleton className="aspect-video w-full" />
)}
{statsHistory.length != 0 ? (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">Detector CPU Usage</div>
{detCpuSeries.map((series) => (
<ThresholdBarGraph
@ -407,10 +407,10 @@ export default function GeneralMetrics({
))}
</div>
) : (
<Skeleton className="w-full aspect-video" />
<Skeleton className="aspect-video w-full" />
)}
{statsHistory.length != 0 ? (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">Detector Memory Usage</div>
{detMemSeries.map((series) => (
<ThresholdBarGraph
@ -425,14 +425,14 @@ export default function GeneralMetrics({
))}
</div>
) : (
<Skeleton className="w-full aspect-video" />
<Skeleton className="aspect-video w-full" />
)}
</div>
{(statsHistory.length == 0 || statsHistory[0].gpu_usages) && (
<>
<div className="mt-4 flex items-center justify-between">
<div className="text-muted-foreground text-sm font-medium">
<div className="text-sm font-medium text-muted-foreground">
GPUs
</div>
{canGetGpuInfo && (
@ -445,9 +445,9 @@ export default function GeneralMetrics({
</Button>
)}
</div>
<div className=" mt-4 grid grid-cols-1 sm:grid-cols-2 gap-2">
<div className=" mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2">
{statsHistory.length != 0 ? (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">GPU Usage</div>
{gpuSeries.map((series) => (
<ThresholdBarGraph
@ -462,12 +462,12 @@ export default function GeneralMetrics({
))}
</div>
) : (
<Skeleton className="w-full aspect-video" />
<Skeleton className="aspect-video w-full" />
)}
{statsHistory.length != 0 ? (
<>
{gpuMemSeries && (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">GPU Memory</div>
{gpuMemSeries.map((series) => (
<ThresholdBarGraph
@ -484,18 +484,18 @@ export default function GeneralMetrics({
)}
</>
) : (
<Skeleton className="w-full aspect-video" />
<Skeleton className="aspect-video w-full" />
)}
</div>
</>
)}
<div className="mt-4 text-muted-foreground text-sm font-medium">
<div className="mt-4 text-sm font-medium text-muted-foreground">
Other Processes
</div>
<div className="mt-4 grid grid-cols-1 sm:grid-cols-2 gap-2">
<div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2">
{statsHistory.length != 0 ? (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">Process CPU Usage</div>
{otherProcessCpuSeries.map((series) => (
<ThresholdBarGraph
@ -510,10 +510,10 @@ export default function GeneralMetrics({
))}
</div>
) : (
<Skeleton className="w-full aspect-tall" />
<Skeleton className="aspect-tall w-full" />
)}
{statsHistory.length != 0 ? (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl">
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">Process Memory Usage</div>
{otherProcessMemSeries.map((series) => (
<ThresholdBarGraph
@ -528,7 +528,7 @@ export default function GeneralMetrics({
))}
</div>
) : (
<Skeleton className="w-full aspect-tall" />
<Skeleton className="aspect-tall w-full" />
)}
</div>
</div>

View File

@ -42,10 +42,10 @@ export default function StorageMetrics({
}
return (
<div className="size-full mt-4 flex flex-col overflow-y-auto">
<div className="text-muted-foreground text-sm font-medium">Overview</div>
<div className="mt-4 grid grid-cols-1 sm:grid-cols-3 gap-2">
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl flex-col">
<div className="mt-4 flex size-full flex-col overflow-y-auto">
<div className="text-sm font-medium text-muted-foreground">Overview</div>
<div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-3">
<div className="flex-col rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">Recordings</div>
<StorageGraph
graphId="general-recordings"
@ -53,7 +53,7 @@ export default function StorageMetrics({
total={totalStorage.total}
/>
</div>
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl flex-col">
<div className="flex-col rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">/tmp/cache</div>
<StorageGraph
graphId="general-cache"
@ -61,7 +61,7 @@ export default function StorageMetrics({
total={stats.service.storage["/tmp/cache"]["total"]}
/>
</div>
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl flex-col">
<div className="flex-col rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5">/dev/shm</div>
<StorageGraph
graphId="general-shared-memory"
@ -70,12 +70,12 @@ export default function StorageMetrics({
/>
</div>
</div>
<div className="mt-4 text-muted-foreground text-sm font-medium">
<div className="mt-4 text-sm font-medium text-muted-foreground">
Camera Storage
</div>
<div className="mt-4 grid grid-cols-1 sm:grid-cols-3 gap-2">
<div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-3">
{Object.keys(cameraStorage).map((camera) => (
<div className="p-2.5 bg-background_alt rounded-lg md:rounded-2xl flex-col">
<div className="flex-col rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
<div className="mb-5 capitalize">{camera.replaceAll("_", " ")}</div>
<StorageGraph
graphId={`${camera}-storage`}