mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix: Add more sanity checks & rename buttons to Graphs
This commit is contained in:
parent
fdc444ed61
commit
225f608556
@ -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",
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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);
|
@ -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);
|
@ -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);
|
Loading…
x
Reference in New Issue
Block a user