Files
frigate/docs/src/components/ModelConfigDropdown/index.jsx
T

131 lines
4.1 KiB
React
Raw Normal View History

2026-06-25 20:46:48 -06:00
import React, { useState } from "react";
import CodeBlock from "@theme/CodeBlock";
import ConfigTabs from "@site/src/components/ConfigTabs";
import TabItem from "@theme/TabItem";
import { marked } from "marked";
import styles from "./styles.module.css";
marked.setOptions({ gfm: true });
/**
* @typedef {Object} Model
* @property {string} key
* @property {string} label
* @property {boolean} recommended
* @property {string} download Markdown for the "download the model" step.
* @property {string} ui Markdown for the Frigate UI configuration step.
* @property {string} yaml Raw YAML for the configuration step.
*/
2026-06-26 06:24:15 -06:00
// marked does not understand Docusaurus admonitions (:::warning ... :::), so
// render those blocks ourselves and marked-parse everything around them.
function renderMarkdown(md) {
if (!md) return "";
const admonition = /:::(\w+)[ \t]*([^\n]*)\n([\s\S]*?)\n:::/g;
let html = "";
let lastIndex = 0;
let match;
while ((match = admonition.exec(md)) !== null) {
html += marked.parse(md.slice(lastIndex, match.index));
const [, type, title, body] = match;
const heading = (title || type).trim();
html +=
`<div class="${styles.admonition} ${styles[`admonition_${type}`] || ""}">` +
`<div class="${styles.admonitionTitle}">${heading}</div>` +
marked.parse(body) +
`</div>`;
lastIndex = admonition.lastIndex;
}
html += marked.parse(md.slice(lastIndex));
return html;
}
2026-06-25 20:46:48 -06:00
function Markdown({ children }) {
return (
<div
className={styles.markdown}
2026-06-26 06:24:15 -06:00
dangerouslySetInnerHTML={{ __html: renderMarkdown(children) }}
2026-06-25 20:46:48 -06:00
/>
);
}
2026-06-26 06:21:35 -06:00
function RecommendedBadge() {
return <span className={styles.recommendedBadge}>Recommended</span>;
}
2026-06-25 20:46:48 -06:00
/**
* @param {{ models: Model[] }} props
*/
export default function ModelConfigDropdown({ models }) {
const [selectedModelIndex, setSelectedModelIndex] = useState(0);
const [isOpen, setIsOpen] = useState(false);
const selectedModel = models[selectedModelIndex];
const hasChoices = models.length > 1;
const handleModelSelect = (index) => {
setSelectedModelIndex(index);
setIsOpen(false);
};
return (
<div className={styles.wrapper}>
<div className={styles.panel}>
<div className={styles.step}>
2026-06-26 06:21:35 -06:00
<h4 className={styles.stepTitle}>Step 1 — Choose a model</h4>
<div
className={`${styles.dropdown} ${isOpen ? styles.open : ""} ${
hasChoices ? "" : styles.static
}`}
onClick={hasChoices ? () => setIsOpen(!isOpen) : undefined}
>
<div className={styles.dropdownContent}>
<span className={styles.modelName}>
{selectedModel.label}
{selectedModel.recommended && <RecommendedBadge />}
</span>
{hasChoices && (
<span className={styles.arrow}>{isOpen ? "▲" : "▼"}</span>
)}
</div>
</div>
{isOpen && hasChoices && (
<div className={styles.menu}>
{models.map((model, index) => (
<div
key={model.key}
className={`${styles.menuItem} ${
index === selectedModelIndex ? styles.menuItemActive : ""
}`}
onClick={() => handleModelSelect(index)}
>
{model.label}
{model.recommended && <RecommendedBadge />}
</div>
))}
</div>
)}
</div>
<div className={styles.step}>
<h4 className={styles.stepTitle}>Step 2 — Download the model</h4>
2026-06-25 20:46:48 -06:00
<Markdown>{selectedModel.download}</Markdown>
</div>
<div className={styles.step}>
2026-06-26 06:21:35 -06:00
<h4 className={styles.stepTitle}>Step 3 — Configure the detector</h4>
2026-06-25 20:46:48 -06:00
<ConfigTabs>
<TabItem value="ui">
<Markdown>{selectedModel.ui}</Markdown>
</TabItem>
<TabItem value="yaml">
<CodeBlock language="yaml">{selectedModel.yaml}</CodeBlock>
</TabItem>
</ConfigTabs>
</div>
</div>
</div>
);
}