Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
import { useEffect } from "react";
|
|
|
|
|
import { Trans, useTranslation } from "react-i18next";
|
|
|
|
|
import { Link, useNavigate } from "react-router-dom";
|
2025-03-17 21:44:57 +03:00
|
|
|
import useSWR from "swr";
|
|
|
|
|
import { CheckCircle2, XCircle } from "lucide-react";
|
Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
import { LuExternalLink } from "react-icons/lu";
|
|
|
|
|
import { Toaster } from "@/components/ui/sonner";
|
|
|
|
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
2025-03-24 18:19:58 +03:00
|
|
|
import { Button } from "@/components/ui/button";
|
Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
import Heading from "@/components/ui/heading";
|
2026-02-27 18:55:36 +03:00
|
|
|
import {
|
|
|
|
|
SettingsGroupCard,
|
|
|
|
|
SplitCardRow,
|
|
|
|
|
} from "@/components/card/SettingsGroupCard";
|
|
|
|
|
import FrigatePlusCurrentModelSummary from "@/views/settings/components/FrigatePlusCurrentModelSummary";
|
Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
import { useDocDomain } from "@/hooks/use-doc-domain";
|
|
|
|
|
import { CameraNameLabel } from "@/components/camera/FriendlyNameLabel";
|
|
|
|
|
import { FrigateConfig } from "@/types/frigateConfig";
|
|
|
|
|
import type { SettingsPageProps } from "@/views/settings/SingleSectionPage";
|
2025-03-24 18:19:58 +03:00
|
|
|
|
Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
export default function FrigatePlusSettingsView(_props: SettingsPageProps) {
|
2025-03-17 21:44:57 +03:00
|
|
|
const { t } = useTranslation("views/settings");
|
2025-05-28 15:10:45 +03:00
|
|
|
const { getLocaleDocUrl } = useDocDomain();
|
Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
const { data: config } = useSWR<FrigateConfig>("config");
|
|
|
|
|
const navigate = useNavigate();
|
2025-03-17 21:44:57 +03:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
document.title = t("documentTitle.frigatePlus");
|
|
|
|
|
}, [t]);
|
|
|
|
|
|
2025-03-24 18:19:58 +03:00
|
|
|
if (!config) {
|
|
|
|
|
return <ActivityIndicator />;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-17 21:44:57 +03:00
|
|
|
return (
|
2026-05-12 19:20:39 +03:00
|
|
|
<div className="flex size-full flex-col md:pr-2">
|
2026-02-27 18:55:36 +03:00
|
|
|
<Toaster position="top-center" closeButton={true} />
|
2026-05-12 19:20:39 +03:00
|
|
|
<div className="w-full max-w-5xl space-y-6 pt-2">
|
|
|
|
|
<div className="flex flex-col gap-0">
|
|
|
|
|
<Heading as="h4" className="mb-2">
|
|
|
|
|
{t("frigatePlus.title")}
|
|
|
|
|
</Heading>
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
|
|
|
|
{t("frigatePlus.description")}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2025-03-17 21:44:57 +03:00
|
|
|
|
2026-05-12 19:20:39 +03:00
|
|
|
<div className="space-y-6">
|
|
|
|
|
<SettingsGroupCard title={t("frigatePlus.cardTitles.api")}>
|
|
|
|
|
<SplitCardRow
|
|
|
|
|
label={t("frigatePlus.apiKey.title")}
|
|
|
|
|
description={
|
|
|
|
|
<>
|
|
|
|
|
<p>{t("frigatePlus.apiKey.desc")}</p>
|
|
|
|
|
{!config?.model.plus && (
|
|
|
|
|
<div className="mt-2 flex items-center text-primary-variant">
|
|
|
|
|
<Link
|
|
|
|
|
to="https://frigate.video/plus"
|
|
|
|
|
target="_blank"
|
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
|
className="inline"
|
|
|
|
|
>
|
|
|
|
|
{t("frigatePlus.apiKey.plusLink")}
|
|
|
|
|
<LuExternalLink className="ml-2 inline-flex size-3" />
|
|
|
|
|
</Link>
|
2026-02-27 18:55:36 +03:00
|
|
|
</div>
|
2026-05-12 19:20:39 +03:00
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
}
|
|
|
|
|
content={
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
{config?.plus?.enabled ? (
|
|
|
|
|
<CheckCircle2 className="h-5 w-5 text-green-500" />
|
|
|
|
|
) : (
|
|
|
|
|
<XCircle className="h-5 w-5 text-red-500" />
|
|
|
|
|
)}
|
|
|
|
|
<span className="text-sm">
|
|
|
|
|
{config?.plus?.enabled
|
|
|
|
|
? t("frigatePlus.apiKey.validated")
|
|
|
|
|
: t("frigatePlus.apiKey.notValidated")}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</SettingsGroupCard>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
2026-05-12 19:20:39 +03:00
|
|
|
{config?.plus?.enabled && (
|
Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
<FrigatePlusCurrentModelSummary
|
|
|
|
|
plusModel={config.model.plus}
|
|
|
|
|
action={
|
|
|
|
|
<Button
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="outline"
|
|
|
|
|
onClick={() =>
|
|
|
|
|
navigate("/settings?page=systemDetectorsAndModel")
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
{t("frigatePlus.changeInDetectorsAndModel")}
|
|
|
|
|
</Button>
|
|
|
|
|
}
|
|
|
|
|
/>
|
2026-05-12 19:20:39 +03:00
|
|
|
)}
|
2025-03-17 21:44:57 +03:00
|
|
|
|
2026-05-12 19:20:39 +03:00
|
|
|
<SettingsGroupCard title={t("frigatePlus.cardTitles.configuration")}>
|
|
|
|
|
<SplitCardRow
|
|
|
|
|
label={t("frigatePlus.snapshotConfig.title")}
|
|
|
|
|
description={
|
|
|
|
|
<>
|
|
|
|
|
<p>
|
|
|
|
|
<Trans ns="views/settings">
|
|
|
|
|
frigatePlus.snapshotConfig.desc
|
|
|
|
|
</Trans>
|
|
|
|
|
</p>
|
|
|
|
|
<div className="mt-2 flex items-center text-primary-variant">
|
|
|
|
|
<Link
|
|
|
|
|
to={getLocaleDocUrl("plus/faq")}
|
|
|
|
|
target="_blank"
|
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
|
className="inline"
|
|
|
|
|
>
|
|
|
|
|
{t("readTheDocumentation", { ns: "common" })}
|
|
|
|
|
<LuExternalLink className="ml-2 inline-flex size-3" />
|
|
|
|
|
</Link>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
}
|
|
|
|
|
content={
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<div className="overflow-x-auto">
|
|
|
|
|
<table className="w-full text-sm">
|
|
|
|
|
<thead>
|
|
|
|
|
<tr className="border-b border-secondary">
|
|
|
|
|
<th className="px-4 py-2 text-left">
|
|
|
|
|
{t("frigatePlus.snapshotConfig.table.camera")}
|
|
|
|
|
</th>
|
|
|
|
|
<th className="px-4 py-2 text-center">
|
|
|
|
|
{t("frigatePlus.snapshotConfig.table.snapshots")}
|
|
|
|
|
</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
{Object.entries(config.cameras).map(
|
|
|
|
|
([name, camera]) => (
|
|
|
|
|
<tr
|
|
|
|
|
key={name}
|
|
|
|
|
className="border-b border-secondary"
|
|
|
|
|
>
|
|
|
|
|
<td className="px-4 py-2">
|
|
|
|
|
<CameraNameLabel camera={name} />
|
|
|
|
|
</td>
|
|
|
|
|
<td className="px-4 py-2 text-center">
|
|
|
|
|
{camera.snapshots.enabled ? (
|
|
|
|
|
<CheckCircle2 className="mx-auto size-5 text-green-500" />
|
|
|
|
|
) : (
|
|
|
|
|
<XCircle className="mx-auto size-5 text-danger" />
|
2026-02-27 18:55:36 +03:00
|
|
|
)}
|
2026-05-12 19:20:39 +03:00
|
|
|
</td>
|
2026-02-27 18:55:36 +03:00
|
|
|
</tr>
|
2026-05-12 19:20:39 +03:00
|
|
|
),
|
|
|
|
|
)}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</SettingsGroupCard>
|
2026-02-27 18:55:36 +03:00
|
|
|
</div>
|
2026-05-12 19:20:39 +03:00
|
|
|
</div>
|
2026-02-27 18:55:36 +03:00
|
|
|
</div>
|
2025-03-17 21:44:57 +03:00
|
|
|
);
|
|
|
|
|
}
|