fix: Add more sanity checks & rename buttons to Graphs

This commit is contained in:
blessedcoolant 2023-07-23 16:49:52 +12:00
parent fdc444ed61
commit 225f608556
5 changed files with 39 additions and 28 deletions

View File

@ -702,9 +702,9 @@
}, },
"nodes": { "nodes": {
"reloadSchema": "Reload Schema", "reloadSchema": "Reload Schema",
"saveNodes": "Save Nodes", "saveGraph": "Save Graph",
"loadNodes": "Load Nodes", "loadGraph": "Load Graph (saved from Node Editor) (Do not copy-paste metadata)",
"clearNodes": "Clear Nodes", "clearGraph": "Clear Graph",
"zoomInNodes": "Zoom In", "zoomInNodes": "Zoom In",
"zoomOutNodes": "Zoom Out", "zoomOutNodes": "Zoom Out",
"fitViewportNodes": "Fit View", "fitViewportNodes": "Fit View",

View File

@ -2,11 +2,11 @@ import { HStack } from '@chakra-ui/react';
import CancelButton from 'features/parameters/components/ProcessButtons/CancelButton'; import CancelButton from 'features/parameters/components/ProcessButtons/CancelButton';
import { memo } from 'react'; import { memo } from 'react';
import { Panel } from 'reactflow'; import { Panel } from 'reactflow';
import LoadNodesButton from '../ui/LoadNodesButton'; import ClearGraphButton from '../ui/ClearGraphButton';
import LoadGraphButton from '../ui/LoadGraphButton';
import NodeInvokeButton from '../ui/NodeInvokeButton'; import NodeInvokeButton from '../ui/NodeInvokeButton';
import ReloadSchemaButton from '../ui/ReloadSchemaButton'; import ReloadSchemaButton from '../ui/ReloadSchemaButton';
import SaveNodesButton from '../ui/SaveNodesButton'; import SaveGraphButton from '../ui/SaveGraphButton';
import ClearNodesButton from '../ui/ClearNodesButton';
const TopCenterPanel = () => { const TopCenterPanel = () => {
return ( return (
@ -15,9 +15,9 @@ const TopCenterPanel = () => {
<NodeInvokeButton /> <NodeInvokeButton />
<CancelButton /> <CancelButton />
<ReloadSchemaButton /> <ReloadSchemaButton />
<SaveNodesButton /> <SaveGraphButton />
<LoadNodesButton /> <LoadGraphButton />
<ClearNodesButton /> <ClearGraphButton />
</HStack> </HStack>
</Panel> </Panel>
); );

View File

@ -9,17 +9,17 @@ import {
Text, Text,
useDisclosure, useDisclosure,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { makeToast } from 'features/system/util/makeToast';
import { RootState } from 'app/store/store'; import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton'; import IAIIconButton from 'common/components/IAIIconButton';
import { nodeEditorReset } from 'features/nodes/store/nodesSlice'; import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { memo, useRef, useCallback } from 'react'; import { makeToast } from 'features/system/util/makeToast';
import { memo, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FaTrash } from 'react-icons/fa'; import { FaTrash } from 'react-icons/fa';
const ClearNodesButton = () => { const ClearGraphButton = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
@ -46,8 +46,8 @@ const ClearNodesButton = () => {
<> <>
<IAIIconButton <IAIIconButton
icon={<FaTrash />} icon={<FaTrash />}
tooltip={t('nodes.clearNodes')} tooltip={t('nodes.clearGraph')}
aria-label={t('nodes.clearNodes')} aria-label={t('nodes.clearGraph')}
onClick={onOpen} onClick={onOpen}
isDisabled={nodes.length === 0} isDisabled={nodes.length === 0}
/> />
@ -62,11 +62,11 @@ const ClearNodesButton = () => {
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold"> <AlertDialogHeader fontSize="lg" fontWeight="bold">
{t('nodes.clearNodes')} {t('nodes.clearGraph')}
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogBody> <AlertDialogBody>
<Text>{t('common.clearNodes')}</Text> <Text>{t('common.clearGraph')}</Text>
</AlertDialogBody> </AlertDialogBody>
<AlertDialogFooter> <AlertDialogFooter>
@ -83,4 +83,4 @@ const ClearNodesButton = () => {
); );
}; };
export default memo(ClearNodesButton); export default memo(ClearGraphButton);

View File

@ -13,17 +13,28 @@ interface JsonFile {
[key: string]: unknown; [key: string]: unknown;
} }
function validateInvokeAIGraph(jsonFile: JsonFile): boolean { function sanityCheckInvokeAIGraph(jsonFile: JsonFile): boolean {
const keys = ['nodes', 'edges', 'viewport']; const keys = ['nodes', 'edges', 'viewport'];
for (const key of keys) { for (const key of keys) {
if (!(key in jsonFile)) { if (!(key in jsonFile)) {
return false; return false;
} }
} }
if (!Array.isArray(jsonFile.nodes) || !Array.isArray(jsonFile.edges)) {
return false;
}
for (const node of jsonFile.nodes) {
if (!('data' in node)) {
return false;
}
}
return true; return true;
} }
const LoadNodesButton = () => { const LoadGraphButton = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { fitView } = useReactFlow(); const { fitView } = useReactFlow();
@ -39,9 +50,9 @@ const LoadNodesButton = () => {
try { try {
const retrievedNodeTree = await JSON.parse(String(json)); const retrievedNodeTree = await JSON.parse(String(json));
const isValidNodeTree = validateInvokeAIGraph(retrievedNodeTree); const isSaneNodeTree = sanityCheckInvokeAIGraph(retrievedNodeTree);
if (isValidNodeTree) { if (isSaneNodeTree) {
dispatch(loadFileNodes(retrievedNodeTree.nodes)); dispatch(loadFileNodes(retrievedNodeTree.nodes));
dispatch(loadFileEdges(retrievedNodeTree.edges)); dispatch(loadFileEdges(retrievedNodeTree.edges));
fitView(); fitView();
@ -93,8 +104,8 @@ const LoadNodesButton = () => {
{(props) => ( {(props) => (
<IAIIconButton <IAIIconButton
icon={<FaUpload />} icon={<FaUpload />}
tooltip={t('nodes.loadNodes')} tooltip={t('nodes.loadGraph')}
aria-label={t('nodes.loadNodes')} aria-label={t('nodes.loadGraph')}
{...props} {...props}
/> />
)} )}
@ -102,4 +113,4 @@ const LoadNodesButton = () => {
); );
}; };
export default memo(LoadNodesButton); export default memo(LoadGraphButton);

View File

@ -6,7 +6,7 @@ import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FaSave } from 'react-icons/fa'; import { FaSave } from 'react-icons/fa';
const SaveNodesButton = () => { const SaveGraphButton = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const editorInstance = useAppSelector( const editorInstance = useAppSelector(
(state: RootState) => state.nodes.editorInstance (state: RootState) => state.nodes.editorInstance
@ -37,12 +37,12 @@ const SaveNodesButton = () => {
<IAIIconButton <IAIIconButton
icon={<FaSave />} icon={<FaSave />}
fontSize={18} fontSize={18}
tooltip={t('nodes.saveNodes')} tooltip={t('nodes.saveGraph')}
aria-label={t('nodes.saveNodes')} aria-label={t('nodes.saveGraph')}
onClick={saveEditorToJSON} onClick={saveEditorToJSON}
isDisabled={nodes.length === 0} isDisabled={nodes.length === 0}
/> />
); );
}; };
export default memo(SaveNodesButton); export default memo(SaveGraphButton);