mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into feat/taesd
This commit is contained in:
commit
578e682562
@ -619,56 +619,174 @@
|
|||||||
"addNodeToolTip": "Add Node (Shift+A, Space)",
|
"addNodeToolTip": "Add Node (Shift+A, Space)",
|
||||||
"animatedEdges": "Animated Edges",
|
"animatedEdges": "Animated Edges",
|
||||||
"animatedEdgesHelp": "Animate selected edges and edges connected to selected nodes",
|
"animatedEdgesHelp": "Animate selected edges and edges connected to selected nodes",
|
||||||
|
"boolean": "Booleans",
|
||||||
|
"booleanCollection": "Boolean Collection",
|
||||||
|
"booleanCollectionDescription": "A collection of booleans.",
|
||||||
|
"booleanDescription": "Booleans are true or false.",
|
||||||
|
"booleanPolymorphic": "Boolean Polymorphic",
|
||||||
|
"booleanPolymorphicDescription": "A collection of booleans.",
|
||||||
"cannotConnectInputToInput": "Cannot connect input to input",
|
"cannotConnectInputToInput": "Cannot connect input to input",
|
||||||
"cannotConnectOutputToOutput": "Cannot connect output to output",
|
"cannotConnectOutputToOutput": "Cannot connect output to output",
|
||||||
"cannotConnectToSelf": "Cannot connect to self",
|
"cannotConnectToSelf": "Cannot connect to self",
|
||||||
|
"clipField": "Clip",
|
||||||
|
"clipFieldDescription": "Tokenizer and text_encoder submodels.",
|
||||||
|
"collection": "Collection",
|
||||||
|
"collectionDescription": "TODO",
|
||||||
|
"collectionItem": "Collection Item",
|
||||||
|
"collectionItemDescription": "TODO",
|
||||||
"colorCodeEdges": "Color-Code Edges",
|
"colorCodeEdges": "Color-Code Edges",
|
||||||
"colorCodeEdgesHelp": "Color-code edges according to their connected fields",
|
"colorCodeEdgesHelp": "Color-code edges according to their connected fields",
|
||||||
|
"colorCollectionDescription": "A collection of colors.",
|
||||||
|
"colorField": "Color",
|
||||||
|
"colorFieldDescription": "A RGBA color.",
|
||||||
|
"colorPolymorphic": "Color Polymorphic",
|
||||||
|
"colorPolymorphicDescription": "A collection of colors.",
|
||||||
|
"conditioningCollection": "Conditioning Collection",
|
||||||
|
"conditioningCollectionDescription": "Conditioning may be passed between nodes.",
|
||||||
|
"conditioningField": "Conditioning",
|
||||||
|
"conditioningFieldDescription": "Conditioning may be passed between nodes.",
|
||||||
|
"conditioningPolymorphic": "Conditioning Polymorphic",
|
||||||
|
"conditioningPolymorphicDescription": "Conditioning may be passed between nodes.",
|
||||||
"connectionWouldCreateCycle": "Connection would create a cycle",
|
"connectionWouldCreateCycle": "Connection would create a cycle",
|
||||||
|
"controlCollection": "Control Collection",
|
||||||
|
"controlCollectionDescription": "Control info passed between nodes.",
|
||||||
|
"controlField": "Control",
|
||||||
|
"controlFieldDescription": "Control info passed between nodes.",
|
||||||
"currentImage": "Current Image",
|
"currentImage": "Current Image",
|
||||||
"currentImageDescription": "Displays the current image in the Node Editor",
|
"currentImageDescription": "Displays the current image in the Node Editor",
|
||||||
|
"denoiseMaskField": "Denoise Mask",
|
||||||
|
"denoiseMaskFieldDescription": "Denoise Mask may be passed between nodes",
|
||||||
|
"doesNotExist": "does not exist",
|
||||||
"downloadWorkflow": "Download Workflow JSON",
|
"downloadWorkflow": "Download Workflow JSON",
|
||||||
|
"edge": "Edge",
|
||||||
|
"enum": "Enum",
|
||||||
|
"enumDescription": "Enums are values that may be one of a number of options.",
|
||||||
|
"executionStateCompleted": "Completed",
|
||||||
|
"executionStateError": "Error",
|
||||||
|
"executionStateInProgress": "In Progress",
|
||||||
"fieldTypesMustMatch": "Field types must match",
|
"fieldTypesMustMatch": "Field types must match",
|
||||||
"fitViewportNodes": "Fit View",
|
"fitViewportNodes": "Fit View",
|
||||||
|
"float": "Float",
|
||||||
|
"floatCollection": "Float Collection",
|
||||||
|
"floatCollectionDescription": "A collection of floats.",
|
||||||
|
"floatDescription": "Floats are numbers with a decimal point.",
|
||||||
|
"floatPolymorphic": "Float Polymorphic",
|
||||||
|
"floatPolymorphicDescription": "A collection of floats.",
|
||||||
"fullyContainNodes": "Fully Contain Nodes to Select",
|
"fullyContainNodes": "Fully Contain Nodes to Select",
|
||||||
"fullyContainNodesHelp": "Nodes must be fully inside the selection box to be selected",
|
"fullyContainNodesHelp": "Nodes must be fully inside the selection box to be selected",
|
||||||
"hideGraphNodes": "Hide Graph Overlay",
|
"hideGraphNodes": "Hide Graph Overlay",
|
||||||
"hideLegendNodes": "Hide Field Type Legend",
|
"hideLegendNodes": "Hide Field Type Legend",
|
||||||
"hideMinimapnodes": "Hide MiniMap",
|
"hideMinimapnodes": "Hide MiniMap",
|
||||||
|
"imageCollection": "Image Collection",
|
||||||
|
"imageCollectionDescription": "A collection of images.",
|
||||||
|
"imageField": "Image",
|
||||||
|
"imageFieldDescription": "Images may be passed between nodes.",
|
||||||
|
"imagePolymorphic": "Image Polymorphic",
|
||||||
|
"imagePolymorphicDescription": "A collection of images.",
|
||||||
|
"inputFields": "Input Feilds",
|
||||||
"inputMayOnlyHaveOneConnection": "Input may only have one connection",
|
"inputMayOnlyHaveOneConnection": "Input may only have one connection",
|
||||||
|
"inputNode": "Input Node",
|
||||||
|
"integer": "Integer",
|
||||||
|
"integerCollection": "Integer Collection",
|
||||||
|
"integerCollectionDescription": "A collection of integers.",
|
||||||
|
"integerDescription": "Integers are whole numbers, without a decimal point.",
|
||||||
|
"integerPolymorphic": "Integer Polymorphic",
|
||||||
|
"integerPolymorphicDescription": "A collection of integers.",
|
||||||
|
"invalidOutputSchema": "Invalid output schema",
|
||||||
|
"latentsCollection": "Latents Collection",
|
||||||
|
"latentsCollectionDescription": "Latents may be passed between nodes.",
|
||||||
|
"latentsField": "Latents",
|
||||||
|
"latentsFieldDescription": "Latents may be passed between nodes.",
|
||||||
|
"latentsPolymorphic": "Latents Polymorphic",
|
||||||
|
"latentsPolymorphicDescription": "Latents may be passed between nodes.",
|
||||||
"loadingNodes": "Loading Nodes...",
|
"loadingNodes": "Loading Nodes...",
|
||||||
"loadWorkflow": "Load Workflow",
|
"loadWorkflow": "Load Workflow",
|
||||||
|
"loRAModelField": "LoRA",
|
||||||
|
"loRAModelFieldDescription": "TODO",
|
||||||
|
"mainModelField": "Model",
|
||||||
|
"mainModelFieldDescription": "TODO",
|
||||||
|
"maybeIncompatible": "May be Incompatible With Installed",
|
||||||
|
"mismatchedVersion": "Has Mismatched Version",
|
||||||
|
"missingCanvaInitImage": "Missing canvas init image",
|
||||||
|
"missingCanvaInitMaskImages": "Missing canvas init and mask images",
|
||||||
|
"missingTemplate": "Missing Template",
|
||||||
"noConnectionData": "No connection data",
|
"noConnectionData": "No connection data",
|
||||||
"noConnectionInProgress": "No connection in progress",
|
"noConnectionInProgress": "No connection in progress",
|
||||||
|
"node": "Node",
|
||||||
"nodeOutputs": "Node Outputs",
|
"nodeOutputs": "Node Outputs",
|
||||||
"nodeSearch": "Search for nodes",
|
"nodeSearch": "Search for nodes",
|
||||||
"nodeTemplate": "Node Template",
|
"nodeTemplate": "Node Template",
|
||||||
|
"nodeType": "Node Type",
|
||||||
"noFieldsLinearview": "No fields added to Linear View",
|
"noFieldsLinearview": "No fields added to Linear View",
|
||||||
"noFieldType": "No field type",
|
"noFieldType": "No field type",
|
||||||
|
"noImageFoundState": "No initial image found in state",
|
||||||
"noMatchingNodes": "No matching nodes",
|
"noMatchingNodes": "No matching nodes",
|
||||||
"noNodeSelected": "No node selected",
|
"noNodeSelected": "No node selected",
|
||||||
"noOpacity": "Node Opacity",
|
"noOpacity": "Node Opacity",
|
||||||
"noOutputRecorded": "No outputs recorded",
|
"noOutputRecorded": "No outputs recorded",
|
||||||
|
"noOutputSchemaName": "No output schema name found in ref object",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"notesDescription": "Add notes about your workflow",
|
"notesDescription": "Add notes about your workflow",
|
||||||
|
"oNNXModelField": "ONNX Model",
|
||||||
|
"oNNXModelFieldDescription": "ONNX model field.",
|
||||||
|
"outputFields": "Output Feilds",
|
||||||
|
"outputNode": "Output node",
|
||||||
|
"outputSchemaNotFound": "Output schema not found",
|
||||||
"pickOne": "Pick One",
|
"pickOne": "Pick One",
|
||||||
|
"problemReadingMetadata": "Problem reading metadata from image",
|
||||||
|
"problemReadingWorkflow": "Problem reading workflow from image",
|
||||||
"problemSettingTitle": "Problem Setting Title",
|
"problemSettingTitle": "Problem Setting Title",
|
||||||
"reloadNodeTemplates": "Reload Node Templates",
|
"reloadNodeTemplates": "Reload Node Templates",
|
||||||
"removeLinearView": "Remove from Linear View",
|
"removeLinearView": "Remove from Linear View",
|
||||||
"resetWorkflow": "Reset Workflow",
|
"resetWorkflow": "Reset Workflow",
|
||||||
"resetWorkflowDesc": "Are you sure you want to reset this workflow?",
|
"resetWorkflowDesc": "Are you sure you want to reset this workflow?",
|
||||||
"resetWorkflowDesc2": "Resetting the workflow will clear all nodes, edges and workflow details.",
|
"resetWorkflowDesc2": "Resetting the workflow will clear all nodes, edges and workflow details.",
|
||||||
|
"scheduler": "Scheduler",
|
||||||
|
"schedulerDescription": "TODO",
|
||||||
|
"sDXLMainModelField": "SDXL Model",
|
||||||
|
"sDXLMainModelFieldDescription": "SDXL model field.",
|
||||||
|
"sDXLRefinerModelField": "Refiner Model",
|
||||||
|
"sDXLRefinerModelFieldDescription": "TODO",
|
||||||
"showGraphNodes": "Show Graph Overlay",
|
"showGraphNodes": "Show Graph Overlay",
|
||||||
"showLegendNodes": "Show Field Type Legend",
|
"showLegendNodes": "Show Field Type Legend",
|
||||||
"showMinimapnodes": "Show MiniMap",
|
"showMinimapnodes": "Show MiniMap",
|
||||||
|
"skipped": "Skipped",
|
||||||
|
"skippedReservedInput": "Skipped reserved input field",
|
||||||
|
"skippedReservedOutput": "Skipped reserved output field",
|
||||||
|
"skippingInputNoTemplate": "Skipping input field with no template",
|
||||||
|
"skippingReservedFieldType": "Skipping reserved field type",
|
||||||
|
"skippingUnknownInputType": "Skipping unknown input field type",
|
||||||
|
"skippingUnknownOutputType": "Skipping unknown output field type",
|
||||||
"snapToGrid": "Snap to Grid",
|
"snapToGrid": "Snap to Grid",
|
||||||
"snapToGridHelp": "Snap nodes to grid when moved",
|
"snapToGridHelp": "Snap nodes to grid when moved",
|
||||||
|
"sourceNode": "Source node",
|
||||||
|
"string": "String",
|
||||||
|
"stringCollection": "String Collection",
|
||||||
|
"stringCollectionDescription": "A collection of strings.",
|
||||||
|
"stringDescription": "Strings are text.",
|
||||||
|
"stringPolymorphic": "String Polymorphic",
|
||||||
|
"stringPolymorphicDescription": "A collection of strings.",
|
||||||
"unableToLoadWorkflow": "Unable to Validate Workflow",
|
"unableToLoadWorkflow": "Unable to Validate Workflow",
|
||||||
|
"unableToParseEdge": "Unable to parse edge",
|
||||||
|
"unableToParseNode": "Unable to parse node",
|
||||||
"unableToValidateWorkflow": "Unable to Validate Workflow",
|
"unableToValidateWorkflow": "Unable to Validate Workflow",
|
||||||
|
"uNetField": "UNet",
|
||||||
|
"uNetFieldDescription": "UNet submodel.",
|
||||||
|
"unhandledInputProperty": "Unhandled input property",
|
||||||
|
"unhandledOutputProperty": "Unhandled output property",
|
||||||
"unknownField": "Unknown Field",
|
"unknownField": "Unknown Field",
|
||||||
|
"unknownNode": "Unknown Node",
|
||||||
|
"unknownTemplate": "Unknown Template",
|
||||||
"unkownInvocation": "Unknown Invocation type",
|
"unkownInvocation": "Unknown Invocation type",
|
||||||
|
"updateApp": "Update App",
|
||||||
|
"vaeField": "Vae",
|
||||||
|
"vaeFieldDescription": "Vae submodel.",
|
||||||
|
"vaeModelField": "VAE",
|
||||||
|
"vaeModelFieldDescription": "TODO",
|
||||||
"validateConnections": "Validate Connections and Graph",
|
"validateConnections": "Validate Connections and Graph",
|
||||||
"validateConnectionsHelp": "Prevent invalid connections from being made, and invalid graphs from being invoked",
|
"validateConnectionsHelp": "Prevent invalid connections from being made, and invalid graphs from being invoked",
|
||||||
|
"version": "Version",
|
||||||
|
"versionUnknown": " Version Unknown",
|
||||||
"workflow": "Workflow",
|
"workflow": "Workflow",
|
||||||
"workflowAuthor": "Author",
|
"workflowAuthor": "Author",
|
||||||
"workflowContact": "Contact",
|
"workflowContact": "Contact",
|
||||||
@ -680,15 +798,7 @@
|
|||||||
"workflowValidation": "Workflow Validation Error",
|
"workflowValidation": "Workflow Validation Error",
|
||||||
"workflowVersion": "Version",
|
"workflowVersion": "Version",
|
||||||
"zoomInNodes": "Zoom In",
|
"zoomInNodes": "Zoom In",
|
||||||
"zoomOutNodes": "Zoom Out",
|
"zoomOutNodes": "Zoom Out"
|
||||||
"executionStateError": "Error",
|
|
||||||
"executionStateCompleted": "Completed",
|
|
||||||
"executionStateInProgress": "In Progress",
|
|
||||||
"versionUnknown": " Version Unknown",
|
|
||||||
"unknownNode": "Unknown Node",
|
|
||||||
"version": "Version",
|
|
||||||
"updateApp": "Update App",
|
|
||||||
"unknownTemplate": "Unknown Template"
|
|
||||||
},
|
},
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"aspectRatio": "Ratio",
|
"aspectRatio": "Ratio",
|
||||||
@ -853,8 +963,14 @@
|
|||||||
"useSlidersForAll": "Use Sliders For All Options"
|
"useSlidersForAll": "Use Sliders For All Options"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
|
"addedToBoard": "Added to board",
|
||||||
|
"baseModelChangedCleared": "Base model changed, cleared",
|
||||||
"canceled": "Processing Canceled",
|
"canceled": "Processing Canceled",
|
||||||
|
"canvasCopiedClipboard": "Canvas Copied to Clipboard",
|
||||||
|
"canvasDownloaded": "Canvas Downloaded",
|
||||||
"canvasMerged": "Canvas Merged",
|
"canvasMerged": "Canvas Merged",
|
||||||
|
"canvasSavedGallery": "Canvas Saved to Gallery",
|
||||||
|
"canvasSentControlnetAssets": "Canvas Sent to ControlNet & Assets",
|
||||||
"connected": "Connected to Server",
|
"connected": "Connected to Server",
|
||||||
"disconnected": "Disconnected from Server",
|
"disconnected": "Disconnected from Server",
|
||||||
"downloadImageStarted": "Image Download Started",
|
"downloadImageStarted": "Image Download Started",
|
||||||
@ -863,10 +979,18 @@
|
|||||||
"imageLinkCopied": "Image Link Copied",
|
"imageLinkCopied": "Image Link Copied",
|
||||||
"imageNotLoaded": "No Image Loaded",
|
"imageNotLoaded": "No Image Loaded",
|
||||||
"imageNotLoadedDesc": "Could not find image",
|
"imageNotLoadedDesc": "Could not find image",
|
||||||
|
"imageSaved": "Image Saved",
|
||||||
"imageSavedToGallery": "Image Saved to Gallery",
|
"imageSavedToGallery": "Image Saved to Gallery",
|
||||||
|
"imageSavingFailed": "Image Saving Failed",
|
||||||
|
"imageUploaded": "Image Uploaded",
|
||||||
|
"imageUploadFailed": "Image Upload Failed",
|
||||||
|
"incompatibleSubmodel": "incompatible submodel",
|
||||||
"initialImageNotSet": "Initial Image Not Set",
|
"initialImageNotSet": "Initial Image Not Set",
|
||||||
"initialImageNotSetDesc": "Could not load initial image",
|
"initialImageNotSetDesc": "Could not load initial image",
|
||||||
"initialImageSet": "Initial Image Set",
|
"initialImageSet": "Initial Image Set",
|
||||||
|
"loadedWithWarnings": "Workflow Loaded with Warnings",
|
||||||
|
"maskSavedAssets": "Mask Saved to Assets",
|
||||||
|
"maskSentControlnetAssets": "Mask Sent to ControlNet & Assets",
|
||||||
"metadataLoadFailed": "Failed to load metadata",
|
"metadataLoadFailed": "Failed to load metadata",
|
||||||
"modelAdded": "Model Added: {{modelName}}",
|
"modelAdded": "Model Added: {{modelName}}",
|
||||||
"modelAddedSimple": "Model Added",
|
"modelAddedSimple": "Model Added",
|
||||||
@ -887,8 +1011,20 @@
|
|||||||
"parametersNotSet": "Parameters Not Set",
|
"parametersNotSet": "Parameters Not Set",
|
||||||
"parametersNotSetDesc": "No metadata found for this image.",
|
"parametersNotSetDesc": "No metadata found for this image.",
|
||||||
"parametersSet": "Parameters Set",
|
"parametersSet": "Parameters Set",
|
||||||
|
"problemCopyingCanvas": "Problem Copying Canvas",
|
||||||
|
"problemCopyingCanvasDesc": "Unable to export base layer",
|
||||||
"problemCopyingImage": "Unable to Copy Image",
|
"problemCopyingImage": "Unable to Copy Image",
|
||||||
"problemCopyingImageLink": "Unable to Copy Image Link",
|
"problemCopyingImageLink": "Unable to Copy Image Link",
|
||||||
|
"problemDownloadingCanvas": "Problem Downloading Canvas",
|
||||||
|
"problemDownloadingCanvasDesc": "Unable to export base layer",
|
||||||
|
"problemImportingMask": "Problem Importing Mask",
|
||||||
|
"problemImportingMaskDesc": "Unable to export mask",
|
||||||
|
"problemMergingCanvas": "Problem Merging Canvas",
|
||||||
|
"problemMergingCanvasDesc": "Unable to export base layer",
|
||||||
|
"problemSavingCanvas": "Problem Saving Canvas",
|
||||||
|
"problemSavingCanvasDesc": "Unable to export base layer",
|
||||||
|
"problemSavingMask": "Problem Saving Mask",
|
||||||
|
"problemSavingMaskDesc": "Unable to export mask",
|
||||||
"promptNotSet": "Prompt Not Set",
|
"promptNotSet": "Prompt Not Set",
|
||||||
"promptNotSetDesc": "Could not find prompt for this image.",
|
"promptNotSetDesc": "Could not find prompt for this image.",
|
||||||
"promptSet": "Prompt Set",
|
"promptSet": "Prompt Set",
|
||||||
@ -898,11 +1034,16 @@
|
|||||||
"sentToImageToImage": "Sent To Image To Image",
|
"sentToImageToImage": "Sent To Image To Image",
|
||||||
"sentToUnifiedCanvas": "Sent to Unified Canvas",
|
"sentToUnifiedCanvas": "Sent to Unified Canvas",
|
||||||
"serverError": "Server Error",
|
"serverError": "Server Error",
|
||||||
|
"setCanvasInitialImage": "Set as canvas initial image",
|
||||||
|
"setControlImage": "Set as control image",
|
||||||
|
"setInitialImage": "Set as initial image",
|
||||||
|
"setNodeField": "Set as node field",
|
||||||
"tempFoldersEmptied": "Temp Folder Emptied",
|
"tempFoldersEmptied": "Temp Folder Emptied",
|
||||||
"uploadFailed": "Upload failed",
|
"uploadFailed": "Upload failed",
|
||||||
"uploadFailedInvalidUploadDesc": "Must be single PNG or JPEG image",
|
"uploadFailedInvalidUploadDesc": "Must be single PNG or JPEG image",
|
||||||
"uploadFailedUnableToLoadDesc": "Unable to load file",
|
"uploadFailedUnableToLoadDesc": "Unable to load file",
|
||||||
"upscalingFailed": "Upscaling Failed"
|
"upscalingFailed": "Upscaling Failed",
|
||||||
|
"workflowLoaded": "Workflow Loaded"
|
||||||
},
|
},
|
||||||
"tooltip": {
|
"tooltip": {
|
||||||
"feature": {
|
"feature": {
|
||||||
|
@ -4,6 +4,7 @@ import { $logger } from 'app/logging/logger';
|
|||||||
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { copyBlobToClipboard } from 'features/canvas/util/copyBlobToClipboard';
|
import { copyBlobToClipboard } from 'features/canvas/util/copyBlobToClipboard';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const addCanvasCopiedToClipboardListener = () => {
|
export const addCanvasCopiedToClipboardListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -20,8 +21,8 @@ export const addCanvasCopiedToClipboardListener = () => {
|
|||||||
moduleLog.error('Problem getting base layer blob');
|
moduleLog.error('Problem getting base layer blob');
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Problem Copying Canvas',
|
title: t('toast.problemCopyingCanvas'),
|
||||||
description: 'Unable to export base layer',
|
description: t('toast.problemCopyingCanvasDesc'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -32,7 +33,7 @@ export const addCanvasCopiedToClipboardListener = () => {
|
|||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Canvas Copied to Clipboard',
|
title: t('toast.canvasCopiedClipboard'),
|
||||||
status: 'success',
|
status: 'success',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -4,6 +4,7 @@ import { $logger } from 'app/logging/logger';
|
|||||||
import { downloadBlob } from 'features/canvas/util/downloadBlob';
|
import { downloadBlob } from 'features/canvas/util/downloadBlob';
|
||||||
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const addCanvasDownloadedAsImageListener = () => {
|
export const addCanvasDownloadedAsImageListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -20,8 +21,8 @@ export const addCanvasDownloadedAsImageListener = () => {
|
|||||||
moduleLog.error('Problem getting base layer blob');
|
moduleLog.error('Problem getting base layer blob');
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Problem Downloading Canvas',
|
title: t('toast.problemDownloadingCanvas'),
|
||||||
description: 'Unable to export base layer',
|
description: t('toast.problemDownloadingCanvasDesc'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -29,7 +30,9 @@ export const addCanvasDownloadedAsImageListener = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
downloadBlob(blob, 'canvas.png');
|
downloadBlob(blob, 'canvas.png');
|
||||||
dispatch(addToast({ title: 'Canvas Downloaded', status: 'success' }));
|
dispatch(
|
||||||
|
addToast({ title: t('toast.canvasDownloaded'), status: 'success' })
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@ import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlic
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const addCanvasImageToControlNetListener = () => {
|
export const addCanvasImageToControlNetListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -19,8 +20,8 @@ export const addCanvasImageToControlNetListener = () => {
|
|||||||
log.error('Problem getting base layer blob');
|
log.error('Problem getting base layer blob');
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Problem Saving Canvas',
|
title: t('toast.problemSavingCanvas'),
|
||||||
description: 'Unable to export base layer',
|
description: t('toast.problemSavingCanvasDesc'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -40,7 +41,7 @@ export const addCanvasImageToControlNetListener = () => {
|
|||||||
crop_visible: true,
|
crop_visible: true,
|
||||||
postUploadAction: {
|
postUploadAction: {
|
||||||
type: 'TOAST',
|
type: 'TOAST',
|
||||||
toastOptions: { title: 'Canvas Sent to ControlNet & Assets' },
|
toastOptions: { title: t('toast.canvasSentControlnetAssets') },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
@ -4,6 +4,7 @@ import { getCanvasData } from 'features/canvas/util/getCanvasData';
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const addCanvasMaskSavedToGalleryListener = () => {
|
export const addCanvasMaskSavedToGalleryListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -30,8 +31,8 @@ export const addCanvasMaskSavedToGalleryListener = () => {
|
|||||||
log.error('Problem getting mask layer blob');
|
log.error('Problem getting mask layer blob');
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Problem Saving Mask',
|
title: t('toast.problemSavingMask'),
|
||||||
description: 'Unable to export mask',
|
description: t('toast.problemSavingMaskDesc'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -51,7 +52,7 @@ export const addCanvasMaskSavedToGalleryListener = () => {
|
|||||||
crop_visible: true,
|
crop_visible: true,
|
||||||
postUploadAction: {
|
postUploadAction: {
|
||||||
type: 'TOAST',
|
type: 'TOAST',
|
||||||
toastOptions: { title: 'Mask Saved to Assets' },
|
toastOptions: { title: t('toast.maskSavedAssets') },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -5,6 +5,7 @@ import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlic
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const addCanvasMaskToControlNetListener = () => {
|
export const addCanvasMaskToControlNetListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -31,8 +32,8 @@ export const addCanvasMaskToControlNetListener = () => {
|
|||||||
log.error('Problem getting mask layer blob');
|
log.error('Problem getting mask layer blob');
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Problem Importing Mask',
|
title: t('toast.problemImportingMask'),
|
||||||
description: 'Unable to export mask',
|
description: t('toast.problemImportingMaskDesc'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -52,7 +53,7 @@ export const addCanvasMaskToControlNetListener = () => {
|
|||||||
crop_visible: true,
|
crop_visible: true,
|
||||||
postUploadAction: {
|
postUploadAction: {
|
||||||
type: 'TOAST',
|
type: 'TOAST',
|
||||||
toastOptions: { title: 'Mask Sent to ControlNet & Assets' },
|
toastOptions: { title: t('toast.maskSentControlnetAssets') },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
@ -6,6 +6,7 @@ import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const addCanvasMergedListener = () => {
|
export const addCanvasMergedListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -20,8 +21,8 @@ export const addCanvasMergedListener = () => {
|
|||||||
moduleLog.error('Problem getting base layer blob');
|
moduleLog.error('Problem getting base layer blob');
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Problem Merging Canvas',
|
title: t('toast.problemMergingCanvas'),
|
||||||
description: 'Unable to export base layer',
|
description: t('toast.problemMergingCanvasDesc'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -34,8 +35,8 @@ export const addCanvasMergedListener = () => {
|
|||||||
moduleLog.error('Problem getting canvas base layer');
|
moduleLog.error('Problem getting canvas base layer');
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Problem Merging Canvas',
|
title: t('toast.problemMergingCanvas'),
|
||||||
description: 'Unable to export base layer',
|
description: t('toast.problemMergingCanvasDesc'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -55,7 +56,7 @@ export const addCanvasMergedListener = () => {
|
|||||||
is_intermediate: true,
|
is_intermediate: true,
|
||||||
postUploadAction: {
|
postUploadAction: {
|
||||||
type: 'TOAST',
|
type: 'TOAST',
|
||||||
toastOptions: { title: 'Canvas Merged' },
|
toastOptions: { title: t('toast.canvasMerged') },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
@ -4,6 +4,7 @@ import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const addCanvasSavedToGalleryListener = () => {
|
export const addCanvasSavedToGalleryListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -18,8 +19,8 @@ export const addCanvasSavedToGalleryListener = () => {
|
|||||||
log.error('Problem getting base layer blob');
|
log.error('Problem getting base layer blob');
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Problem Saving Canvas',
|
title: t('toast.problemSavingCanvas'),
|
||||||
description: 'Unable to export base layer',
|
description: t('toast.problemSavingCanvasDesc'),
|
||||||
status: 'error',
|
status: 'error',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -39,7 +40,7 @@ export const addCanvasSavedToGalleryListener = () => {
|
|||||||
crop_visible: true,
|
crop_visible: true,
|
||||||
postUploadAction: {
|
postUploadAction: {
|
||||||
type: 'TOAST',
|
type: 'TOAST',
|
||||||
toastOptions: { title: 'Canvas Saved to Gallery' },
|
toastOptions: { title: t('toast.canvasSavedGallery') },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -9,9 +9,10 @@ import { omit } from 'lodash-es';
|
|||||||
import { boardsApi } from 'services/api/endpoints/boards';
|
import { boardsApi } from 'services/api/endpoints/boards';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
import { imagesApi } from '../../../../../services/api/endpoints/images';
|
import { imagesApi } from '../../../../../services/api/endpoints/images';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
const DEFAULT_UPLOADED_TOAST: UseToastOptions = {
|
const DEFAULT_UPLOADED_TOAST: UseToastOptions = {
|
||||||
title: 'Image Uploaded',
|
title: t('toast.imageUploaded'),
|
||||||
status: 'success',
|
status: 'success',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,8 +58,8 @@ export const addImageUploadedFulfilledListener = () => {
|
|||||||
// Fall back to just the board id if we can't find the board for some reason
|
// Fall back to just the board id if we can't find the board for some reason
|
||||||
const board = data?.find((b) => b.board_id === autoAddBoardId);
|
const board = data?.find((b) => b.board_id === autoAddBoardId);
|
||||||
const description = board
|
const description = board
|
||||||
? `Added to board ${board.board_name}`
|
? `${t('toast.addedToBoard')} ${board.board_name}`
|
||||||
: `Added to board ${autoAddBoardId}`;
|
: `${t('toast.addedToBoard')} ${autoAddBoardId}`;
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
@ -75,7 +76,7 @@ export const addImageUploadedFulfilledListener = () => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: 'Set as canvas initial image',
|
description: t('toast.setCanvasInitialImage'),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -92,7 +93,7 @@ export const addImageUploadedFulfilledListener = () => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: 'Set as control image',
|
description: t('toast.setControlImage'),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -103,7 +104,7 @@ export const addImageUploadedFulfilledListener = () => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: 'Set as initial image',
|
description: t('toast.setInitialImage'),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -117,7 +118,7 @@ export const addImageUploadedFulfilledListener = () => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: `Set as node field ${fieldName}`,
|
description: `${t('toast.setNodeField')} ${fieldName}`,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -140,7 +141,7 @@ export const addImageUploadedRejectedListener = () => {
|
|||||||
log.error({ ...sanitizedData }, 'Image upload failed');
|
log.error({ ...sanitizedData }, 'Image upload failed');
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Image Upload Failed',
|
title: t('toast.imageUploadFailed'),
|
||||||
description: action.error.message,
|
description: action.error.message,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
})
|
})
|
||||||
|
@ -14,6 +14,7 @@ import { addToast } from 'features/system/store/systemSlice';
|
|||||||
import { makeToast } from 'features/system/util/makeToast';
|
import { makeToast } from 'features/system/util/makeToast';
|
||||||
import { forEach } from 'lodash-es';
|
import { forEach } from 'lodash-es';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const addModelSelectedListener = () => {
|
export const addModelSelectedListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -67,7 +68,9 @@ export const addModelSelectedListener = () => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
makeToast({
|
makeToast({
|
||||||
title: `Base model changed, cleared ${modelsCleared} incompatible submodel${
|
title: `${t(
|
||||||
|
'toast.baseModelChangedCleared'
|
||||||
|
)} ${modelsCleared} ${t('toast.incompatibleSubmodel')}${
|
||||||
modelsCleared === 1 ? '' : 's'
|
modelsCleared === 1 ? '' : 's'
|
||||||
}`,
|
}`,
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
|
@ -2,6 +2,7 @@ import { stagingAreaImageSaved } from 'features/canvas/store/actions';
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const addStagingAreaImageSavedListener = () => {
|
export const addStagingAreaImageSavedListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -28,11 +29,11 @@ export const addStagingAreaImageSavedListener = () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
dispatch(addToast({ title: 'Image Saved', status: 'success' }));
|
dispatch(addToast({ title: t('toast.imageSaved'), status: 'success' }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
title: 'Image Saving Failed',
|
title: t('toast.imageSavingFailed'),
|
||||||
description: (error as Error)?.message,
|
description: (error as Error)?.message,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
})
|
})
|
||||||
|
@ -7,6 +7,7 @@ import { addToast } from 'features/system/store/systemSlice';
|
|||||||
import { makeToast } from 'features/system/util/makeToast';
|
import { makeToast } from 'features/system/util/makeToast';
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||||
import { startAppListening } from '..';
|
import { startAppListening } from '..';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const addWorkflowLoadedListener = () => {
|
export const addWorkflowLoadedListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -27,7 +28,7 @@ export const addWorkflowLoadedListener = () => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
makeToast({
|
makeToast({
|
||||||
title: 'Workflow Loaded',
|
title: t('toast.workflowLoaded'),
|
||||||
status: 'success',
|
status: 'success',
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -36,7 +37,7 @@ export const addWorkflowLoadedListener = () => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
makeToast({
|
makeToast({
|
||||||
title: 'Workflow Loaded with Warnings',
|
title: t('toast.loadedWithWarnings'),
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { CoreMetadata } from 'features/nodes/types/types';
|
import { CoreMetadata, LoRAMetadataItem } from 'features/nodes/types/types';
|
||||||
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import ImageMetadataItem from './ImageMetadataItem';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { isValidLoRAModel } from '../../../parameters/types/parameterSchemas';
|
||||||
|
import ImageMetadataItem from './ImageMetadataItem';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
metadata?: CoreMetadata;
|
metadata?: CoreMetadata;
|
||||||
@ -24,6 +25,7 @@ const ImageMetadataActions = (props: Props) => {
|
|||||||
recallWidth,
|
recallWidth,
|
||||||
recallHeight,
|
recallHeight,
|
||||||
recallStrength,
|
recallStrength,
|
||||||
|
recallLoRA,
|
||||||
} = useRecallParameters();
|
} = useRecallParameters();
|
||||||
|
|
||||||
const handleRecallPositivePrompt = useCallback(() => {
|
const handleRecallPositivePrompt = useCallback(() => {
|
||||||
@ -66,6 +68,13 @@ const ImageMetadataActions = (props: Props) => {
|
|||||||
recallStrength(metadata?.strength);
|
recallStrength(metadata?.strength);
|
||||||
}, [metadata?.strength, recallStrength]);
|
}, [metadata?.strength, recallStrength]);
|
||||||
|
|
||||||
|
const handleRecallLoRA = useCallback(
|
||||||
|
(lora: LoRAMetadataItem) => {
|
||||||
|
recallLoRA(lora);
|
||||||
|
},
|
||||||
|
[recallLoRA]
|
||||||
|
);
|
||||||
|
|
||||||
if (!metadata || Object.keys(metadata).length === 0) {
|
if (!metadata || Object.keys(metadata).length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -130,20 +139,6 @@ const ImageMetadataActions = (props: Props) => {
|
|||||||
onClick={handleRecallHeight}
|
onClick={handleRecallHeight}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* {metadata.threshold !== undefined && (
|
|
||||||
<MetadataItem
|
|
||||||
label={t('metadata.threshold')}
|
|
||||||
value={metadata.threshold}
|
|
||||||
onClick={() => dispatch(setThreshold(Number(metadata.threshold)))}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{metadata.perlin !== undefined && (
|
|
||||||
<MetadataItem
|
|
||||||
label={t('metadata.perlin')}
|
|
||||||
value={metadata.perlin}
|
|
||||||
onClick={() => dispatch(setPerlin(Number(metadata.perlin)))}
|
|
||||||
/>
|
|
||||||
)} */}
|
|
||||||
{metadata.scheduler && (
|
{metadata.scheduler && (
|
||||||
<ImageMetadataItem
|
<ImageMetadataItem
|
||||||
label={t('metadata.scheduler')}
|
label={t('metadata.scheduler')}
|
||||||
@ -165,40 +160,6 @@ const ImageMetadataActions = (props: Props) => {
|
|||||||
onClick={handleRecallCfgScale}
|
onClick={handleRecallCfgScale}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* {metadata.variations && metadata.variations.length > 0 && (
|
|
||||||
<MetadataItem
|
|
||||||
label="{t('metadata.variations')}
|
|
||||||
value={seedWeightsToString(metadata.variations)}
|
|
||||||
onClick={() =>
|
|
||||||
dispatch(
|
|
||||||
setSeedWeights(seedWeightsToString(metadata.variations))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{metadata.seamless && (
|
|
||||||
<MetadataItem
|
|
||||||
label={t('metadata.seamless')}
|
|
||||||
value={metadata.seamless}
|
|
||||||
onClick={() => dispatch(setSeamless(metadata.seamless))}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{metadata.hires_fix && (
|
|
||||||
<MetadataItem
|
|
||||||
label={t('metadata.hiresFix')}
|
|
||||||
value={metadata.hires_fix}
|
|
||||||
onClick={() => dispatch(setHiresFix(metadata.hires_fix))}
|
|
||||||
/>
|
|
||||||
)} */}
|
|
||||||
|
|
||||||
{/* {init_image_path && (
|
|
||||||
<MetadataItem
|
|
||||||
label={t('metadata.initImage')}
|
|
||||||
value={init_image_path}
|
|
||||||
isLink
|
|
||||||
onClick={() => dispatch(setInitialImage(init_image_path))}
|
|
||||||
/>
|
|
||||||
)} */}
|
|
||||||
{metadata.strength && (
|
{metadata.strength && (
|
||||||
<ImageMetadataItem
|
<ImageMetadataItem
|
||||||
label={t('metadata.strength')}
|
label={t('metadata.strength')}
|
||||||
@ -206,13 +167,19 @@ const ImageMetadataActions = (props: Props) => {
|
|||||||
onClick={handleRecallStrength}
|
onClick={handleRecallStrength}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* {metadata.fit && (
|
{metadata.loras &&
|
||||||
<MetadataItem
|
metadata.loras.map((lora, index) => {
|
||||||
label={t('metadata.fit')}
|
if (isValidLoRAModel(lora.lora)) {
|
||||||
value={metadata.fit}
|
return (
|
||||||
onClick={() => dispatch(setShouldFitToWidthHeight(metadata.fit))}
|
<ImageMetadataItem
|
||||||
|
key={index}
|
||||||
|
label="LoRA"
|
||||||
|
value={`${lora.lora.model_name} - ${lora.weight}`}
|
||||||
|
onClick={() => handleRecallLoRA(lora)}
|
||||||
/>
|
/>
|
||||||
)} */}
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -27,6 +27,13 @@ export const loraSlice = createSlice({
|
|||||||
const { model_name, id, base_model } = action.payload;
|
const { model_name, id, base_model } = action.payload;
|
||||||
state.loras[id] = { id, model_name, base_model, ...defaultLoRAConfig };
|
state.loras[id] = { id, model_name, base_model, ...defaultLoRAConfig };
|
||||||
},
|
},
|
||||||
|
loraRecalled: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<LoRAModelConfigEntity & { weight: number }>
|
||||||
|
) => {
|
||||||
|
const { model_name, id, base_model, weight } = action.payload;
|
||||||
|
state.loras[id] = { id, model_name, base_model, weight };
|
||||||
|
},
|
||||||
loraRemoved: (state, action: PayloadAction<string>) => {
|
loraRemoved: (state, action: PayloadAction<string>) => {
|
||||||
const id = action.payload;
|
const id = action.payload;
|
||||||
delete state.loras[id];
|
delete state.loras[id];
|
||||||
@ -62,6 +69,7 @@ export const {
|
|||||||
loraWeightChanged,
|
loraWeightChanged,
|
||||||
loraWeightReset,
|
loraWeightReset,
|
||||||
lorasCleared,
|
lorasCleared,
|
||||||
|
loraRecalled,
|
||||||
} = loraSlice.actions;
|
} = loraSlice.actions;
|
||||||
|
|
||||||
export default loraSlice.reducer;
|
export default loraSlice.reducer;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { FieldType, FieldUIConfig } from './types';
|
import { FieldType, FieldUIConfig } from './types';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const HANDLE_TOOLTIP_OPEN_DELAY = 500;
|
export const HANDLE_TOOLTIP_OPEN_DELAY = 500;
|
||||||
export const COLOR_TOKEN_VALUE = 500;
|
export const COLOR_TOKEN_VALUE = 500;
|
||||||
@ -102,73 +103,73 @@ export const isPolymorphicItemType = (
|
|||||||
export const FIELDS: Record<FieldType, FieldUIConfig> = {
|
export const FIELDS: Record<FieldType, FieldUIConfig> = {
|
||||||
boolean: {
|
boolean: {
|
||||||
color: 'green.500',
|
color: 'green.500',
|
||||||
description: 'Booleans are true or false.',
|
description: t('nodes.booleanDescription'),
|
||||||
title: 'Boolean',
|
title: t('nodes.boolean'),
|
||||||
},
|
},
|
||||||
BooleanCollection: {
|
BooleanCollection: {
|
||||||
color: 'green.500',
|
color: 'green.500',
|
||||||
description: 'A collection of booleans.',
|
description: t('nodes.booleanCollectionDescription'),
|
||||||
title: 'Boolean Collection',
|
title: t('nodes.booleanCollection'),
|
||||||
},
|
},
|
||||||
BooleanPolymorphic: {
|
BooleanPolymorphic: {
|
||||||
color: 'green.500',
|
color: 'green.500',
|
||||||
description: 'A collection of booleans.',
|
description: t('nodes.booleanPolymorphicDescription'),
|
||||||
title: 'Boolean Polymorphic',
|
title: t('nodes.booleanPolymorphic'),
|
||||||
},
|
},
|
||||||
ClipField: {
|
ClipField: {
|
||||||
color: 'green.500',
|
color: 'green.500',
|
||||||
description: 'Tokenizer and text_encoder submodels.',
|
description: t('nodes.clipFieldDescription'),
|
||||||
title: 'Clip',
|
title: t('nodes.clipField'),
|
||||||
},
|
},
|
||||||
Collection: {
|
Collection: {
|
||||||
color: 'base.500',
|
color: 'base.500',
|
||||||
description: 'TODO',
|
description: t('nodes.collectionDescription'),
|
||||||
title: 'Collection',
|
title: t('nodes.collection'),
|
||||||
},
|
},
|
||||||
CollectionItem: {
|
CollectionItem: {
|
||||||
color: 'base.500',
|
color: 'base.500',
|
||||||
description: 'TODO',
|
description: t('nodes.collectionItemDescription'),
|
||||||
title: 'Collection Item',
|
title: t('nodes.collectionItem'),
|
||||||
},
|
},
|
||||||
ColorCollection: {
|
ColorCollection: {
|
||||||
color: 'pink.300',
|
color: 'pink.300',
|
||||||
description: 'A collection of colors.',
|
description: t('nodes.colorCollectionDescription'),
|
||||||
title: 'Color Collection',
|
title: t('nodes.colorCollection'),
|
||||||
},
|
},
|
||||||
ColorField: {
|
ColorField: {
|
||||||
color: 'pink.300',
|
color: 'pink.300',
|
||||||
description: 'A RGBA color.',
|
description: t('nodes.colorFieldDescription'),
|
||||||
title: 'Color',
|
title: t('nodes.colorField'),
|
||||||
},
|
},
|
||||||
ColorPolymorphic: {
|
ColorPolymorphic: {
|
||||||
color: 'pink.300',
|
color: 'pink.300',
|
||||||
description: 'A collection of colors.',
|
description: t('nodes.colorPolymorphicDescription'),
|
||||||
title: 'Color Polymorphic',
|
title: t('nodes.colorPolymorphic'),
|
||||||
},
|
},
|
||||||
ConditioningCollection: {
|
ConditioningCollection: {
|
||||||
color: 'cyan.500',
|
color: 'cyan.500',
|
||||||
description: 'Conditioning may be passed between nodes.',
|
description: t('nodes.conditioningCollectionDescription'),
|
||||||
title: 'Conditioning Collection',
|
title: t('nodes.conditioningCollection'),
|
||||||
},
|
},
|
||||||
ConditioningField: {
|
ConditioningField: {
|
||||||
color: 'cyan.500',
|
color: 'cyan.500',
|
||||||
description: 'Conditioning may be passed between nodes.',
|
description: t('nodes.conditioningFieldDescription'),
|
||||||
title: 'Conditioning',
|
title: t('nodes.conditioningField'),
|
||||||
},
|
},
|
||||||
ConditioningPolymorphic: {
|
ConditioningPolymorphic: {
|
||||||
color: 'cyan.500',
|
color: 'cyan.500',
|
||||||
description: 'Conditioning may be passed between nodes.',
|
description: t('nodes.conditioningPolymorphicDescription'),
|
||||||
title: 'Conditioning Polymorphic',
|
title: t('nodes.conditioningPolymorphic'),
|
||||||
},
|
},
|
||||||
ControlCollection: {
|
ControlCollection: {
|
||||||
color: 'teal.500',
|
color: 'teal.500',
|
||||||
description: 'Control info passed between nodes.',
|
description: t('nodes.controlCollectionDescription'),
|
||||||
title: 'Control Collection',
|
title: t('nodes.controlCollection'),
|
||||||
},
|
},
|
||||||
ControlField: {
|
ControlField: {
|
||||||
color: 'teal.500',
|
color: 'teal.500',
|
||||||
description: 'Control info passed between nodes.',
|
description: t('nodes.controlFieldDescription'),
|
||||||
title: 'Control',
|
title: t('nodes.controlField'),
|
||||||
},
|
},
|
||||||
ControlNetModelField: {
|
ControlNetModelField: {
|
||||||
color: 'teal.500',
|
color: 'teal.500',
|
||||||
@ -182,132 +183,132 @@ export const FIELDS: Record<FieldType, FieldUIConfig> = {
|
|||||||
},
|
},
|
||||||
DenoiseMaskField: {
|
DenoiseMaskField: {
|
||||||
color: 'blue.300',
|
color: 'blue.300',
|
||||||
description: 'Denoise Mask may be passed between nodes',
|
description: t('nodes.denoiseMaskFieldDescription'),
|
||||||
title: 'Denoise Mask',
|
title: t('nodes.denoiseMaskField'),
|
||||||
},
|
},
|
||||||
enum: {
|
enum: {
|
||||||
color: 'blue.500',
|
color: 'blue.500',
|
||||||
description: 'Enums are values that may be one of a number of options.',
|
description: t('nodes.enumDescription'),
|
||||||
title: 'Enum',
|
title: t('nodes.enum'),
|
||||||
},
|
},
|
||||||
float: {
|
float: {
|
||||||
color: 'orange.500',
|
color: 'orange.500',
|
||||||
description: 'Floats are numbers with a decimal point.',
|
description: t('nodes.floatDescription'),
|
||||||
title: 'Float',
|
title: t('nodes.float'),
|
||||||
},
|
},
|
||||||
FloatCollection: {
|
FloatCollection: {
|
||||||
color: 'orange.500',
|
color: 'orange.500',
|
||||||
description: 'A collection of floats.',
|
description: t('nodes.floatCollectionDescription'),
|
||||||
title: 'Float Collection',
|
title: t('nodes.floatCollection'),
|
||||||
},
|
},
|
||||||
FloatPolymorphic: {
|
FloatPolymorphic: {
|
||||||
color: 'orange.500',
|
color: 'orange.500',
|
||||||
description: 'A collection of floats.',
|
description: t('nodes.floatPolymorphicDescription'),
|
||||||
title: 'Float Polymorphic',
|
title: t('nodes.floatPolymorphic'),
|
||||||
},
|
},
|
||||||
ImageCollection: {
|
ImageCollection: {
|
||||||
color: 'purple.500',
|
color: 'purple.500',
|
||||||
description: 'A collection of images.',
|
description: t('nodes.imageCollectionDescription'),
|
||||||
title: 'Image Collection',
|
title: t('nodes.imageCollection'),
|
||||||
},
|
},
|
||||||
ImageField: {
|
ImageField: {
|
||||||
color: 'purple.500',
|
color: 'purple.500',
|
||||||
description: 'Images may be passed between nodes.',
|
description: t('nodes.imageFieldDescription'),
|
||||||
title: 'Image',
|
title: t('nodes.imageField'),
|
||||||
},
|
},
|
||||||
ImagePolymorphic: {
|
ImagePolymorphic: {
|
||||||
color: 'purple.500',
|
color: 'purple.500',
|
||||||
description: 'A collection of images.',
|
description: t('nodes.imagePolymorphicDescription'),
|
||||||
title: 'Image Polymorphic',
|
title: t('nodes.imagePolymorphic'),
|
||||||
},
|
},
|
||||||
integer: {
|
integer: {
|
||||||
color: 'red.500',
|
color: 'red.500',
|
||||||
description: 'Integers are whole numbers, without a decimal point.',
|
description: t('nodes.integerDescription'),
|
||||||
title: 'Integer',
|
title: t('nodes.integer'),
|
||||||
},
|
},
|
||||||
IntegerCollection: {
|
IntegerCollection: {
|
||||||
color: 'red.500',
|
color: 'red.500',
|
||||||
description: 'A collection of integers.',
|
description: t('nodes.integerCollectionDescription'),
|
||||||
title: 'Integer Collection',
|
title: t('nodes.integerCollection'),
|
||||||
},
|
},
|
||||||
IntegerPolymorphic: {
|
IntegerPolymorphic: {
|
||||||
color: 'red.500',
|
color: 'red.500',
|
||||||
description: 'A collection of integers.',
|
description: t('nodes.integerPolymorphicDescription'),
|
||||||
title: 'Integer Polymorphic',
|
title: t('nodes.integerPolymorphic'),
|
||||||
},
|
},
|
||||||
LatentsCollection: {
|
LatentsCollection: {
|
||||||
color: 'pink.500',
|
color: 'pink.500',
|
||||||
description: 'Latents may be passed between nodes.',
|
description: t('nodes.latentsCollectionDescription'),
|
||||||
title: 'Latents Collection',
|
title: t('nodes.latentsCollection'),
|
||||||
},
|
},
|
||||||
LatentsField: {
|
LatentsField: {
|
||||||
color: 'pink.500',
|
color: 'pink.500',
|
||||||
description: 'Latents may be passed between nodes.',
|
description: t('nodes.latentsFieldDescription'),
|
||||||
title: 'Latents',
|
title: t('nodes.latentsField'),
|
||||||
},
|
},
|
||||||
LatentsPolymorphic: {
|
LatentsPolymorphic: {
|
||||||
color: 'pink.500',
|
color: 'pink.500',
|
||||||
description: 'Latents may be passed between nodes.',
|
description: t('nodes.latentsPolymorphicDescription'),
|
||||||
title: 'Latents Polymorphic',
|
title: t('nodes.latentsPolymorphic'),
|
||||||
},
|
},
|
||||||
LoRAModelField: {
|
LoRAModelField: {
|
||||||
color: 'teal.500',
|
color: 'teal.500',
|
||||||
description: 'TODO',
|
description: t('nodes.loRAModelFieldDescription'),
|
||||||
title: 'LoRA',
|
title: t('nodes.loRAModelField'),
|
||||||
},
|
},
|
||||||
MainModelField: {
|
MainModelField: {
|
||||||
color: 'teal.500',
|
color: 'teal.500',
|
||||||
description: 'TODO',
|
description: t('nodes.mainModelFieldDescription'),
|
||||||
title: 'Model',
|
title: t('nodes.mainModelField'),
|
||||||
},
|
},
|
||||||
ONNXModelField: {
|
ONNXModelField: {
|
||||||
color: 'teal.500',
|
color: 'teal.500',
|
||||||
description: 'ONNX model field.',
|
description: t('nodes.oNNXModelFieldDescription'),
|
||||||
title: 'ONNX Model',
|
title: t('nodes.oNNXModelField'),
|
||||||
},
|
},
|
||||||
Scheduler: {
|
Scheduler: {
|
||||||
color: 'base.500',
|
color: 'base.500',
|
||||||
description: 'TODO',
|
description: t('nodes.schedulerDescription'),
|
||||||
title: 'Scheduler',
|
title: t('nodes.scheduler'),
|
||||||
},
|
},
|
||||||
SDXLMainModelField: {
|
SDXLMainModelField: {
|
||||||
color: 'teal.500',
|
color: 'teal.500',
|
||||||
description: 'SDXL model field.',
|
description: t('nodes.sDXLMainModelFieldDescription'),
|
||||||
title: 'SDXL Model',
|
title: t('nodes.sDXLMainModelField'),
|
||||||
},
|
},
|
||||||
SDXLRefinerModelField: {
|
SDXLRefinerModelField: {
|
||||||
color: 'teal.500',
|
color: 'teal.500',
|
||||||
description: 'TODO',
|
description: t('nodes.sDXLRefinerModelFieldDescription'),
|
||||||
title: 'Refiner Model',
|
title: t('nodes.sDXLRefinerModelField'),
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
color: 'yellow.500',
|
color: 'yellow.500',
|
||||||
description: 'Strings are text.',
|
description: t('nodes.stringDescription'),
|
||||||
title: 'String',
|
title: t('nodes.string'),
|
||||||
},
|
},
|
||||||
StringCollection: {
|
StringCollection: {
|
||||||
color: 'yellow.500',
|
color: 'yellow.500',
|
||||||
description: 'A collection of strings.',
|
description: t('nodes.stringCollectionDescription'),
|
||||||
title: 'String Collection',
|
title: t('nodes.stringCollection'),
|
||||||
},
|
},
|
||||||
StringPolymorphic: {
|
StringPolymorphic: {
|
||||||
color: 'yellow.500',
|
color: 'yellow.500',
|
||||||
description: 'A collection of strings.',
|
description: t('nodes.stringPolymorphicDescription'),
|
||||||
title: 'String Polymorphic',
|
title: t('nodes.stringPolymorphic'),
|
||||||
},
|
},
|
||||||
UNetField: {
|
UNetField: {
|
||||||
color: 'red.500',
|
color: 'red.500',
|
||||||
description: 'UNet submodel.',
|
description: t('nodes.uNetFieldDescription'),
|
||||||
title: 'UNet',
|
title: t('nodes.uNetField'),
|
||||||
},
|
},
|
||||||
VaeField: {
|
VaeField: {
|
||||||
color: 'blue.500',
|
color: 'blue.500',
|
||||||
description: 'Vae submodel.',
|
description: t('nodes.vaeFieldDescription'),
|
||||||
title: 'Vae',
|
title: t('nodes.vaeField'),
|
||||||
},
|
},
|
||||||
VaeModelField: {
|
VaeModelField: {
|
||||||
color: 'teal.500',
|
color: 'teal.500',
|
||||||
description: 'TODO',
|
description: t('nodes.vaeModelFieldDescription'),
|
||||||
title: 'VAE',
|
title: t('nodes.vaeModelField'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
import { O } from 'ts-toolbelt';
|
import { O } from 'ts-toolbelt';
|
||||||
import { JsonObject } from 'type-fest';
|
import { JsonObject } from 'type-fest';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
import i18n from 'i18next';
|
||||||
|
|
||||||
export type NonNullableGraph = O.Required<Graph, 'nodes' | 'edges'>;
|
export type NonNullableGraph = O.Required<Graph, 'nodes' | 'edges'>;
|
||||||
|
|
||||||
@ -1056,6 +1057,13 @@ export const isInvocationFieldSchema = (
|
|||||||
|
|
||||||
export type InvocationEdgeExtra = { type: 'default' | 'collapsed' };
|
export type InvocationEdgeExtra = { type: 'default' | 'collapsed' };
|
||||||
|
|
||||||
|
const zLoRAMetadataItem = z.object({
|
||||||
|
lora: zLoRAModelField.deepPartial(),
|
||||||
|
weight: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type LoRAMetadataItem = z.infer<typeof zLoRAMetadataItem>;
|
||||||
|
|
||||||
export const zCoreMetadata = z
|
export const zCoreMetadata = z
|
||||||
.object({
|
.object({
|
||||||
app_version: z.string().nullish(),
|
app_version: z.string().nullish(),
|
||||||
@ -1075,14 +1083,7 @@ export const zCoreMetadata = z
|
|||||||
.union([zMainModel.deepPartial(), zOnnxModel.deepPartial()])
|
.union([zMainModel.deepPartial(), zOnnxModel.deepPartial()])
|
||||||
.nullish(),
|
.nullish(),
|
||||||
controlnets: z.array(zControlField.deepPartial()).nullish(),
|
controlnets: z.array(zControlField.deepPartial()).nullish(),
|
||||||
loras: z
|
loras: z.array(zLoRAMetadataItem).nullish(),
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
lora: zLoRAModelField.deepPartial(),
|
|
||||||
weight: z.number(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.nullish(),
|
|
||||||
vae: zVaeModelField.nullish(),
|
vae: zVaeModelField.nullish(),
|
||||||
strength: z.number().nullish(),
|
strength: z.number().nullish(),
|
||||||
init_image: z.string().nullish(),
|
init_image: z.string().nullish(),
|
||||||
@ -1258,23 +1259,35 @@ export const zValidatedWorkflow = zWorkflow.transform((workflow) => {
|
|||||||
const targetNode = keyedNodes[edge.target];
|
const targetNode = keyedNodes[edge.target];
|
||||||
const issues: string[] = [];
|
const issues: string[] = [];
|
||||||
if (!sourceNode) {
|
if (!sourceNode) {
|
||||||
issues.push(`Output node ${edge.source} does not exist`);
|
issues.push(
|
||||||
|
`${i18n.t('nodes.outputNode')} ${edge.source} ${i18n.t(
|
||||||
|
'nodes.doesNotExist'
|
||||||
|
)}`
|
||||||
|
);
|
||||||
} else if (
|
} else if (
|
||||||
edge.type === 'default' &&
|
edge.type === 'default' &&
|
||||||
!(edge.sourceHandle in sourceNode.data.outputs)
|
!(edge.sourceHandle in sourceNode.data.outputs)
|
||||||
) {
|
) {
|
||||||
issues.push(
|
issues.push(
|
||||||
`Output field "${edge.source}.${edge.sourceHandle}" does not exist`
|
`${i18n.t('nodes.outputField')}"${edge.source}.${
|
||||||
|
edge.sourceHandle
|
||||||
|
}" ${i18n.t('nodes.doesNotExist')}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!targetNode) {
|
if (!targetNode) {
|
||||||
issues.push(`Input node ${edge.target} does not exist`);
|
issues.push(
|
||||||
|
`${i18n.t('nodes.inputNode')} ${edge.target} ${i18n.t(
|
||||||
|
'nodes.doesNotExist'
|
||||||
|
)}`
|
||||||
|
);
|
||||||
} else if (
|
} else if (
|
||||||
edge.type === 'default' &&
|
edge.type === 'default' &&
|
||||||
!(edge.targetHandle in targetNode.data.inputs)
|
!(edge.targetHandle in targetNode.data.inputs)
|
||||||
) {
|
) {
|
||||||
issues.push(
|
issues.push(
|
||||||
`Input field "${edge.target}.${edge.targetHandle}" does not exist`
|
`${i18n.t('nodes.inputField')} "${edge.target}.${
|
||||||
|
edge.targetHandle
|
||||||
|
}" ${i18n.t('nodes.doesNotExist')}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (issues.length) {
|
if (issues.length) {
|
||||||
@ -1282,7 +1295,9 @@ export const zValidatedWorkflow = zWorkflow.transform((workflow) => {
|
|||||||
const src = edge.type === 'default' ? edge.sourceHandle : edge.source;
|
const src = edge.type === 'default' ? edge.sourceHandle : edge.source;
|
||||||
const tgt = edge.type === 'default' ? edge.targetHandle : edge.target;
|
const tgt = edge.type === 'default' ? edge.targetHandle : edge.target;
|
||||||
warnings.push({
|
warnings.push({
|
||||||
message: `Edge "${src} -> ${tgt}" skipped`,
|
message: `${i18n.t('nodes.edge')} "${src} -> ${tgt}" ${i18n.t(
|
||||||
|
'nodes.skipped'
|
||||||
|
)}`,
|
||||||
issues,
|
issues,
|
||||||
data: edge,
|
data: edge,
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,7 @@ import { NodesState } from '../store/types';
|
|||||||
import { Workflow, zWorkflowEdge, zWorkflowNode } from '../types/types';
|
import { Workflow, zWorkflowEdge, zWorkflowNode } from '../types/types';
|
||||||
import { fromZodError } from 'zod-validation-error';
|
import { fromZodError } from 'zod-validation-error';
|
||||||
import { parseify } from 'common/util/serialize';
|
import { parseify } from 'common/util/serialize';
|
||||||
|
import i18n from 'i18next';
|
||||||
|
|
||||||
export const buildWorkflow = (nodesState: NodesState): Workflow => {
|
export const buildWorkflow = (nodesState: NodesState): Workflow => {
|
||||||
const { workflow: workflowMeta, nodes, edges } = nodesState;
|
const { workflow: workflowMeta, nodes, edges } = nodesState;
|
||||||
@ -20,7 +21,7 @@ export const buildWorkflow = (nodesState: NodesState): Workflow => {
|
|||||||
const result = zWorkflowNode.safeParse(node);
|
const result = zWorkflowNode.safeParse(node);
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
const { message } = fromZodError(result.error, {
|
const { message } = fromZodError(result.error, {
|
||||||
prefix: 'Unable to parse node',
|
prefix: i18n.t('nodes.unableToParseNode'),
|
||||||
});
|
});
|
||||||
logger('nodes').warn({ node: parseify(node) }, message);
|
logger('nodes').warn({ node: parseify(node) }, message);
|
||||||
return;
|
return;
|
||||||
@ -32,7 +33,7 @@ export const buildWorkflow = (nodesState: NodesState): Workflow => {
|
|||||||
const result = zWorkflowEdge.safeParse(edge);
|
const result = zWorkflowEdge.safeParse(edge);
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
const { message } = fromZodError(result.error, {
|
const { message } = fromZodError(result.error, {
|
||||||
prefix: 'Unable to parse edge',
|
prefix: i18n.t('nodes.unableToParseEdge'),
|
||||||
});
|
});
|
||||||
logger('nodes').warn({ edge: parseify(edge) }, message);
|
logger('nodes').warn({ edge: parseify(edge) }, message);
|
||||||
return;
|
return;
|
||||||
|
@ -79,8 +79,8 @@ export const buildCanvasInpaintGraph = (
|
|||||||
} = state.generation;
|
} = state.generation;
|
||||||
|
|
||||||
if (!model) {
|
if (!model) {
|
||||||
log.error('No model found in state');
|
log.error('No Image found in state');
|
||||||
throw new Error('No model found in state');
|
throw new Error('No Image found in state');
|
||||||
}
|
}
|
||||||
|
|
||||||
// The bounding box determines width and height, not the width and height params
|
// The bounding box determines width and height, not the width and height params
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
isWorkflowInvocationNode,
|
isWorkflowInvocationNode,
|
||||||
} from '../types/types';
|
} from '../types/types';
|
||||||
import { parseify } from 'common/util/serialize';
|
import { parseify } from 'common/util/serialize';
|
||||||
|
import i18n from 'i18next';
|
||||||
|
|
||||||
export const validateWorkflow = (
|
export const validateWorkflow = (
|
||||||
workflow: Workflow,
|
workflow: Workflow,
|
||||||
@ -25,8 +26,14 @@ export const validateWorkflow = (
|
|||||||
const nodeTemplate = nodeTemplates[node.data.type];
|
const nodeTemplate = nodeTemplates[node.data.type];
|
||||||
if (!nodeTemplate) {
|
if (!nodeTemplate) {
|
||||||
errors.push({
|
errors.push({
|
||||||
message: `Node "${node.data.type}" skipped`,
|
message: `${i18n.t('nodes.node')} "${node.data.type}" ${i18n.t(
|
||||||
issues: [`Node type "${node.data.type}" does not exist`],
|
'nodes.skipped'
|
||||||
|
)}`,
|
||||||
|
issues: [
|
||||||
|
`${i18n.t('nodes.nodeType')}"${node.data.type}" ${i18n.t(
|
||||||
|
'nodes.doesNotExist'
|
||||||
|
)}`,
|
||||||
|
],
|
||||||
data: node,
|
data: node,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -38,9 +45,13 @@ export const validateWorkflow = (
|
|||||||
compareVersions(nodeTemplate.version, node.data.version) !== 0
|
compareVersions(nodeTemplate.version, node.data.version) !== 0
|
||||||
) {
|
) {
|
||||||
errors.push({
|
errors.push({
|
||||||
message: `Node "${node.data.type}" has mismatched version`,
|
message: `${i18n.t('nodes.node')} "${node.data.type}" ${i18n.t(
|
||||||
|
'nodes.mismatchedVersion'
|
||||||
|
)}`,
|
||||||
issues: [
|
issues: [
|
||||||
`Node "${node.data.type}" v${node.data.version} may be incompatible with installed v${nodeTemplate.version}`,
|
`${i18n.t('nodes.node')} "${node.data.type}" v${
|
||||||
|
node.data.version
|
||||||
|
} ${i18n.t('nodes.maybeIncompatible')} v${nodeTemplate.version}`,
|
||||||
],
|
],
|
||||||
data: { node, nodeTemplate: parseify(nodeTemplate) },
|
data: { node, nodeTemplate: parseify(nodeTemplate) },
|
||||||
});
|
});
|
||||||
@ -52,33 +63,49 @@ export const validateWorkflow = (
|
|||||||
const targetNode = keyedNodes[edge.target];
|
const targetNode = keyedNodes[edge.target];
|
||||||
const issues: string[] = [];
|
const issues: string[] = [];
|
||||||
if (!sourceNode) {
|
if (!sourceNode) {
|
||||||
issues.push(`Output node ${edge.source} does not exist`);
|
issues.push(
|
||||||
|
`${i18n.t('nodes.outputNode')} ${edge.source} ${i18n.t(
|
||||||
|
'nodes.doesNotExist'
|
||||||
|
)}`
|
||||||
|
);
|
||||||
} else if (
|
} else if (
|
||||||
edge.type === 'default' &&
|
edge.type === 'default' &&
|
||||||
!(edge.sourceHandle in sourceNode.data.outputs)
|
!(edge.sourceHandle in sourceNode.data.outputs)
|
||||||
) {
|
) {
|
||||||
issues.push(
|
issues.push(
|
||||||
`Output field "${edge.source}.${edge.sourceHandle}" does not exist`
|
`${i18n.t('nodes.outputNodes')} "${edge.source}.${
|
||||||
|
edge.sourceHandle
|
||||||
|
}" ${i18n.t('nodes.doesNotExist')}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!targetNode) {
|
if (!targetNode) {
|
||||||
issues.push(`Input node ${edge.target} does not exist`);
|
issues.push(
|
||||||
|
`${i18n.t('nodes.inputNode')} ${edge.target} ${i18n.t(
|
||||||
|
'nodes.doesNotExist'
|
||||||
|
)}`
|
||||||
|
);
|
||||||
} else if (
|
} else if (
|
||||||
edge.type === 'default' &&
|
edge.type === 'default' &&
|
||||||
!(edge.targetHandle in targetNode.data.inputs)
|
!(edge.targetHandle in targetNode.data.inputs)
|
||||||
) {
|
) {
|
||||||
issues.push(
|
issues.push(
|
||||||
`Input field "${edge.target}.${edge.targetHandle}" does not exist`
|
`${i18n.t('nodes.inputFeilds')} "${edge.target}.${
|
||||||
|
edge.targetHandle
|
||||||
|
}" ${i18n.t('nodes.doesNotExist')}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!nodeTemplates[sourceNode?.data.type ?? '__UNKNOWN_NODE_TYPE__']) {
|
if (!nodeTemplates[sourceNode?.data.type ?? '__UNKNOWN_NODE_TYPE__']) {
|
||||||
issues.push(
|
issues.push(
|
||||||
`Source node "${edge.source}" missing template "${sourceNode?.data.type}"`
|
`${i18n.t('nodes.sourceNode')} "${edge.source}" ${i18n.t(
|
||||||
|
'nodes.missingTemplate'
|
||||||
|
)} "${sourceNode?.data.type}"`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!nodeTemplates[targetNode?.data.type ?? '__UNKNOWN_NODE_TYPE__']) {
|
if (!nodeTemplates[targetNode?.data.type ?? '__UNKNOWN_NODE_TYPE__']) {
|
||||||
issues.push(
|
issues.push(
|
||||||
`Source node "${edge.target}" missing template "${targetNode?.data.type}"`
|
`${i18n.t('nodes.sourceNode')}"${edge.target}" ${i18n.t(
|
||||||
|
'nodes.missingTemplate'
|
||||||
|
)} "${targetNode?.data.type}"`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (issues.length) {
|
if (issues.length) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppToaster } from 'app/components/Toaster';
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { CoreMetadata } from 'features/nodes/types/types';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { CoreMetadata, LoRAMetadataItem } from 'features/nodes/types/types';
|
||||||
import {
|
import {
|
||||||
refinerModelChanged,
|
refinerModelChanged,
|
||||||
setNegativeStylePromptSDXL,
|
setNegativeStylePromptSDXL,
|
||||||
@ -15,6 +17,11 @@ import {
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ImageDTO } from 'services/api/types';
|
import { ImageDTO } from 'services/api/types';
|
||||||
|
import {
|
||||||
|
loraModelsAdapter,
|
||||||
|
useGetLoRAModelsQuery,
|
||||||
|
} from '../../../services/api/endpoints/models';
|
||||||
|
import { loraRecalled } from '../../lora/store/loraSlice';
|
||||||
import { initialImageSelected, modelSelected } from '../store/actions';
|
import { initialImageSelected, modelSelected } from '../store/actions';
|
||||||
import {
|
import {
|
||||||
setCfgScale,
|
setCfgScale,
|
||||||
@ -30,6 +37,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
isValidCfgScale,
|
isValidCfgScale,
|
||||||
isValidHeight,
|
isValidHeight,
|
||||||
|
isValidLoRAModel,
|
||||||
isValidMainModel,
|
isValidMainModel,
|
||||||
isValidNegativePrompt,
|
isValidNegativePrompt,
|
||||||
isValidPositivePrompt,
|
isValidPositivePrompt,
|
||||||
@ -46,10 +54,16 @@ import {
|
|||||||
isValidWidth,
|
isValidWidth,
|
||||||
} from '../types/parameterSchemas';
|
} from '../types/parameterSchemas';
|
||||||
|
|
||||||
|
const selector = createSelector(stateSelector, ({ generation }) => {
|
||||||
|
const { model } = generation;
|
||||||
|
return { model };
|
||||||
|
});
|
||||||
|
|
||||||
export const useRecallParameters = () => {
|
export const useRecallParameters = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const toaster = useAppToaster();
|
const toaster = useAppToaster();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { model } = useAppSelector(selector);
|
||||||
|
|
||||||
const parameterSetToast = useCallback(() => {
|
const parameterSetToast = useCallback(() => {
|
||||||
toaster({
|
toaster({
|
||||||
@ -60,14 +74,18 @@ export const useRecallParameters = () => {
|
|||||||
});
|
});
|
||||||
}, [t, toaster]);
|
}, [t, toaster]);
|
||||||
|
|
||||||
const parameterNotSetToast = useCallback(() => {
|
const parameterNotSetToast = useCallback(
|
||||||
|
(description?: string) => {
|
||||||
toaster({
|
toaster({
|
||||||
title: t('toast.parameterNotSet'),
|
title: t('toast.parameterNotSet'),
|
||||||
|
description,
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
duration: 2500,
|
duration: 2500,
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
});
|
});
|
||||||
}, [t, toaster]);
|
},
|
||||||
|
[t, toaster]
|
||||||
|
);
|
||||||
|
|
||||||
const allParameterSetToast = useCallback(() => {
|
const allParameterSetToast = useCallback(() => {
|
||||||
toaster({
|
toaster({
|
||||||
@ -78,14 +96,18 @@ export const useRecallParameters = () => {
|
|||||||
});
|
});
|
||||||
}, [t, toaster]);
|
}, [t, toaster]);
|
||||||
|
|
||||||
const allParameterNotSetToast = useCallback(() => {
|
const allParameterNotSetToast = useCallback(
|
||||||
|
(description?: string) => {
|
||||||
toaster({
|
toaster({
|
||||||
title: t('toast.parametersNotSet'),
|
title: t('toast.parametersNotSet'),
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
|
description,
|
||||||
duration: 2500,
|
duration: 2500,
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
});
|
});
|
||||||
}, [t, toaster]);
|
},
|
||||||
|
[t, toaster]
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recall both prompts with toast
|
* Recall both prompts with toast
|
||||||
@ -307,6 +329,67 @@ export const useRecallParameters = () => {
|
|||||||
[dispatch, parameterSetToast, parameterNotSetToast]
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall LoRA with toast
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { loras } = useGetLoRAModelsQuery(undefined, {
|
||||||
|
selectFromResult: (result) => ({
|
||||||
|
loras: result.data
|
||||||
|
? loraModelsAdapter.getSelectors().selectAll(result.data)
|
||||||
|
: [],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const prepareLoRAMetadataItem = useCallback(
|
||||||
|
(loraMetadataItem: LoRAMetadataItem) => {
|
||||||
|
if (!isValidLoRAModel(loraMetadataItem.lora)) {
|
||||||
|
return { lora: null, error: 'Invalid LoRA model' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const { base_model, model_name } = loraMetadataItem.lora;
|
||||||
|
|
||||||
|
const matchingLoRA = loras.find(
|
||||||
|
(l) => l.base_model === base_model && l.model_name === model_name
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!matchingLoRA) {
|
||||||
|
return { lora: null, error: 'LoRA model is not installed' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCompatibleBaseModel =
|
||||||
|
matchingLoRA?.base_model === model?.base_model;
|
||||||
|
|
||||||
|
if (!isCompatibleBaseModel) {
|
||||||
|
return {
|
||||||
|
lora: null,
|
||||||
|
error: 'LoRA incompatible with currently-selected model',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { lora: matchingLoRA, error: null };
|
||||||
|
},
|
||||||
|
[loras, model?.base_model]
|
||||||
|
);
|
||||||
|
|
||||||
|
const recallLoRA = useCallback(
|
||||||
|
(loraMetadataItem: LoRAMetadataItem) => {
|
||||||
|
const result = prepareLoRAMetadataItem(loraMetadataItem);
|
||||||
|
|
||||||
|
if (!result.lora) {
|
||||||
|
parameterNotSetToast(result.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
loraRecalled({ ...result.lora, weight: loraMetadataItem.weight })
|
||||||
|
);
|
||||||
|
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[prepareLoRAMetadataItem, dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sets image as initial image with toast
|
* Sets image as initial image with toast
|
||||||
*/
|
*/
|
||||||
@ -344,6 +427,7 @@ export const useRecallParameters = () => {
|
|||||||
refiner_positive_aesthetic_score,
|
refiner_positive_aesthetic_score,
|
||||||
refiner_negative_aesthetic_score,
|
refiner_negative_aesthetic_score,
|
||||||
refiner_start,
|
refiner_start,
|
||||||
|
loras,
|
||||||
} = metadata;
|
} = metadata;
|
||||||
|
|
||||||
if (isValidCfgScale(cfg_scale)) {
|
if (isValidCfgScale(cfg_scale)) {
|
||||||
@ -425,9 +509,21 @@ export const useRecallParameters = () => {
|
|||||||
dispatch(setRefinerStart(refiner_start));
|
dispatch(setRefinerStart(refiner_start));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loras?.forEach((lora) => {
|
||||||
|
const result = prepareLoRAMetadataItem(lora);
|
||||||
|
if (result.lora) {
|
||||||
|
dispatch(loraRecalled({ ...result.lora, weight: lora.weight }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
allParameterSetToast();
|
allParameterSetToast();
|
||||||
},
|
},
|
||||||
[allParameterNotSetToast, allParameterSetToast, dispatch]
|
[
|
||||||
|
allParameterNotSetToast,
|
||||||
|
allParameterSetToast,
|
||||||
|
dispatch,
|
||||||
|
prepareLoRAMetadataItem,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -444,6 +540,7 @@ export const useRecallParameters = () => {
|
|||||||
recallWidth,
|
recallWidth,
|
||||||
recallHeight,
|
recallHeight,
|
||||||
recallStrength,
|
recallStrength,
|
||||||
|
recallLoRA,
|
||||||
recallAllParameters,
|
recallAllParameters,
|
||||||
sendToImageToImage,
|
sendToImageToImage,
|
||||||
};
|
};
|
||||||
|
@ -80,7 +80,7 @@ export const initialGenerationState: GenerationState = {
|
|||||||
scheduler: 'euler',
|
scheduler: 'euler',
|
||||||
maskBlur: 16,
|
maskBlur: 16,
|
||||||
maskBlurMethod: 'box',
|
maskBlurMethod: 'box',
|
||||||
canvasCoherenceMode: 'edge',
|
canvasCoherenceMode: 'unmasked',
|
||||||
canvasCoherenceSteps: 20,
|
canvasCoherenceSteps: 20,
|
||||||
canvasCoherenceStrength: 0.3,
|
canvasCoherenceStrength: 0.3,
|
||||||
seed: 0,
|
seed: 0,
|
||||||
|
@ -128,7 +128,7 @@ export const mainModelsAdapter = createEntityAdapter<MainModelConfigEntity>({
|
|||||||
const onnxModelsAdapter = createEntityAdapter<OnnxModelConfigEntity>({
|
const onnxModelsAdapter = createEntityAdapter<OnnxModelConfigEntity>({
|
||||||
sortComparer: (a, b) => a.model_name.localeCompare(b.model_name),
|
sortComparer: (a, b) => a.model_name.localeCompare(b.model_name),
|
||||||
});
|
});
|
||||||
const loraModelsAdapter = createEntityAdapter<LoRAModelConfigEntity>({
|
export const loraModelsAdapter = createEntityAdapter<LoRAModelConfigEntity>({
|
||||||
sortComparer: (a, b) => a.model_name.localeCompare(b.model_name),
|
sortComparer: (a, b) => a.model_name.localeCompare(b.model_name),
|
||||||
});
|
});
|
||||||
export const controlNetModelsAdapter =
|
export const controlNetModelsAdapter =
|
||||||
|
@ -198,6 +198,13 @@ output = "coverage/index.xml"
|
|||||||
max-line-length = 120
|
max-line-length = 120
|
||||||
ignore = ["E203", "E266", "E501", "W503"]
|
ignore = ["E203", "E266", "E501", "W503"]
|
||||||
select = ["B", "C", "E", "F", "W", "T4"]
|
select = ["B", "C", "E", "F", "W", "T4"]
|
||||||
|
exclude = [
|
||||||
|
".git",
|
||||||
|
"__pycache__",
|
||||||
|
"build",
|
||||||
|
"dist",
|
||||||
|
"invokeai/frontend/web/node_modules/"
|
||||||
|
]
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 120
|
line-length = 120
|
||||||
|
Loading…
Reference in New Issue
Block a user