mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Support config editor in new webUI (#8928)
* Add config editor to new webUI and fix double scroll * Address review comments and enable light theme for vscode
This commit is contained in:
parent
0e6528a989
commit
c1f14e2d87
122
web-new/package-lock.json
generated
122
web-new/package-lock.json
generated
@ -30,6 +30,7 @@
|
||||
"idb-keyval": "^6.2.1",
|
||||
"immer": "^10.0.3",
|
||||
"lucide-react": "^0.294.0",
|
||||
"monaco-yaml": "^5.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-day-picker": "^8.9.1",
|
||||
"react-dom": "^18.2.0",
|
||||
@ -2199,8 +2200,7 @@
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.10.3",
|
||||
@ -5108,8 +5108,7 @@
|
||||
"node_modules/jsonc-parser": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
|
||||
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w=="
|
||||
},
|
||||
"node_modules/keycode": {
|
||||
"version": "2.2.0",
|
||||
@ -5433,6 +5432,84 @@
|
||||
"integrity": "sha512-5SmjNStN6bSuSE5WPT2ZV+iYn1/yI9sd4Igtk23ChvqB7kDk9lZbB9F5frsuvpB+2njdIeGGFf2G4gbE6rCC9Q==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/monaco-languageserver-types": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/monaco-languageserver-types/-/monaco-languageserver-types-0.2.3.tgz",
|
||||
"integrity": "sha512-QyV5R7s+rJ87bX1sRioMJZULWiTnMp0Vm+RLILgMEL0SqWuBsQBSW0EZunr4xMZhv6Qun3UZNCN4JrCCLURcgQ==",
|
||||
"dependencies": {
|
||||
"monaco-types": "^0.1.0",
|
||||
"vscode-languageserver-protocol": "^3.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/remcohaszing"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-marker-data-provider": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/monaco-marker-data-provider/-/monaco-marker-data-provider-1.1.1.tgz",
|
||||
"integrity": "sha512-PGB7TJSZE5tmHzkxv/OEwK2RGNC2A7dcq4JRJnnj31CUAsfmw0Gl+1QTrH0W0deKhcQmQM0YVPaqgQ+0wCt8Mg==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/remcohaszing"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"monaco-editor": ">=0.30.0"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-types": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/monaco-types/-/monaco-types-0.1.0.tgz",
|
||||
"integrity": "sha512-aWK7SN9hAqNYi0WosPoMjenMeXJjwCxDibOqWffyQ/qXdzB/86xshGQobRferfmNz7BSNQ8GB0MD0oby9/5fTQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/remcohaszing"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-worker-manager": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/monaco-worker-manager/-/monaco-worker-manager-2.0.1.tgz",
|
||||
"integrity": "sha512-kdPL0yvg5qjhKPNVjJoym331PY/5JC11aPJXtCZNwWRvBr6jhkIamvYAyiY5P1AWFmNOy0aRDRoMdZfa71h8kg==",
|
||||
"peerDependencies": {
|
||||
"monaco-editor": ">=0.30.0"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-yaml": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/monaco-yaml/-/monaco-yaml-5.1.0.tgz",
|
||||
"integrity": "sha512-DU+cgXSJdOFKQ4I4oLg0V+mHKq1dJX+7hbIE4fJsgegUf1zEHW3PNlGj6qabUU2HZIPJ5NmXEf005GU9YDzTYQ==",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.0",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"monaco-languageserver-types": "^0.2.0",
|
||||
"monaco-marker-data-provider": "^1.0.0",
|
||||
"monaco-types": "^0.1.0",
|
||||
"monaco-worker-manager": "^2.0.0",
|
||||
"path-browserify": "^1.0.0",
|
||||
"prettier": "^2.0.0",
|
||||
"vscode-languageserver-textdocument": "^1.0.0",
|
||||
"vscode-languageserver-types": "^3.0.0",
|
||||
"vscode-uri": "^3.0.0",
|
||||
"yaml": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/remcohaszing"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"monaco-editor": ">=0.36"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-yaml/node_modules/prettier": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/mpd-parser": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-1.2.2.tgz",
|
||||
@ -5812,6 +5889,11 @@
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@ -7743,6 +7825,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-jsonrpc": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
||||
"integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-protocol": {
|
||||
"version": "3.17.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
|
||||
"integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
|
||||
"dependencies": {
|
||||
"vscode-jsonrpc": "8.2.0",
|
||||
"vscode-languageserver-types": "3.17.5"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-textdocument": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz",
|
||||
"integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA=="
|
||||
},
|
||||
"node_modules/vscode-languageserver-types": {
|
||||
"version": "3.17.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
|
||||
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="
|
||||
},
|
||||
"node_modules/vscode-uri": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz",
|
||||
"integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="
|
||||
},
|
||||
"node_modules/w3c-xmlserializer": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
|
||||
|
@ -35,6 +35,7 @@
|
||||
"idb-keyval": "^6.2.1",
|
||||
"immer": "^10.0.3",
|
||||
"lucide-react": "^0.294.0",
|
||||
"monaco-yaml": "^5.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-day-picker": "^8.9.1",
|
||||
"react-dom": "^18.2.0",
|
||||
|
@ -1,10 +1,156 @@
|
||||
import useSWR from "swr";
|
||||
import * as monaco from "monaco-editor";
|
||||
import { configureMonacoYaml } from "monaco-yaml";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useApiHost } from "@/api";
|
||||
import Heading from "@/components/ui/heading";
|
||||
import ActivityIndicator from "@/components/ui/activity-indicator";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import axios from "axios";
|
||||
import copy from "copy-to-clipboard";
|
||||
import { useTheme } from "@/context/theme-provider";
|
||||
|
||||
type SaveOptions = "saveonly" | "restart";
|
||||
|
||||
function ConfigEditor() {
|
||||
const apiHost = useApiHost();
|
||||
|
||||
const { data: config } = useSWR<string>("config/raw");
|
||||
|
||||
const { theme } = useTheme();
|
||||
const [success, setSuccess] = useState<string | undefined>();
|
||||
const [error, setError] = useState<string | undefined>();
|
||||
|
||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>();
|
||||
const modelRef = useRef<monaco.editor.IEditorModel | null>();
|
||||
|
||||
const onHandleSaveConfig = useCallback(
|
||||
async (save_option: SaveOptions) => {
|
||||
if (!editorRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
axios
|
||||
.post(
|
||||
`config/save?save_option=${save_option}`,
|
||||
editorRef.current.getValue(),
|
||||
{
|
||||
headers: { "Content-Type": "text/plain" },
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
setError("");
|
||||
setSuccess(response.data.message);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
setSuccess("");
|
||||
|
||||
if (error.response) {
|
||||
setError(error.response.data.message);
|
||||
} else {
|
||||
setError(error.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
[editorRef]
|
||||
);
|
||||
|
||||
const handleCopyConfig = useCallback(async () => {
|
||||
if (!editorRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy(editorRef.current.getValue());
|
||||
}, [editorRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (modelRef.current != null) {
|
||||
// we don't need to recreate the editor if it already exists
|
||||
return;
|
||||
}
|
||||
|
||||
const modelUri = monaco.Uri.parse("a://b/api/config/schema.json");
|
||||
|
||||
if (monaco.editor.getModels().length > 0) {
|
||||
modelRef.current = monaco.editor.getModel(modelUri);
|
||||
} else {
|
||||
modelRef.current = monaco.editor.createModel(config, "yaml", modelUri);
|
||||
}
|
||||
|
||||
configureMonacoYaml(monaco, {
|
||||
enableSchemaRequest: true,
|
||||
hover: true,
|
||||
completion: true,
|
||||
validate: true,
|
||||
format: true,
|
||||
schemas: [
|
||||
{
|
||||
uri: `${apiHost}api/config/schema.json`,
|
||||
fileMatch: [String(modelUri)],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const container = document.getElementById("container");
|
||||
|
||||
if (container != undefined) {
|
||||
editorRef.current = monaco.editor.create(container, {
|
||||
language: "yaml",
|
||||
model: modelRef.current,
|
||||
scrollBeyondLastLine: false,
|
||||
theme: theme == "dark" ? "vs-dark" : "vs-light",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!config) {
|
||||
return <ActivityIndicator />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading as="h2">Configuration Editor</Heading>
|
||||
</>
|
||||
<div className="absolute h-[70%] w-[96%] md:h-[85%] md:w-[88%]">
|
||||
<div className="lg:flex justify-between mr-1">
|
||||
<Heading as="h2">Config</Heading>
|
||||
<div>
|
||||
<Button
|
||||
size="sm"
|
||||
className="mx-1"
|
||||
onClick={(_) => handleCopyConfig()}
|
||||
>
|
||||
Copy Config
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="mx-1"
|
||||
onClick={(_) => onHandleSaveConfig("restart")}
|
||||
>
|
||||
Save & Restart
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="mx-1"
|
||||
onClick={(_) => onHandleSaveConfig("saveonly")}
|
||||
>
|
||||
Save Only
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{success && <div className="max-h-20 text-green-500">{success}</div>}
|
||||
{error && (
|
||||
<div className="p-4 overflow-scroll text-red-500 whitespace-pre-wrap">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div id="container" className="h-full mt-2" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user