mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into lstein/config-management-fixes
This commit is contained in:
commit
ff63433591
@ -173,7 +173,7 @@ class TextToLatentsInvocation(BaseInvocation):
|
|||||||
negative_conditioning: Optional[ConditioningField] = Field(description="Negative conditioning for generation")
|
negative_conditioning: Optional[ConditioningField] = Field(description="Negative conditioning for generation")
|
||||||
noise: Optional[LatentsField] = Field(description="The noise to use")
|
noise: Optional[LatentsField] = Field(description="The noise to use")
|
||||||
steps: int = Field(default=10, gt=0, description="The number of steps to use to generate the image")
|
steps: int = Field(default=10, gt=0, description="The number of steps to use to generate the image")
|
||||||
cfg_scale: float = Field(default=7.5, gt=0, description="The Classifier-Free Guidance, higher values may result in a result closer to the prompt", )
|
cfg_scale: float = Field(default=7.5, ge=1, description="The Classifier-Free Guidance, higher values may result in a result closer to the prompt", )
|
||||||
scheduler: SAMPLER_NAME_VALUES = Field(default="euler", description="The scheduler to use" )
|
scheduler: SAMPLER_NAME_VALUES = Field(default="euler", description="The scheduler to use" )
|
||||||
model: str = Field(default="", description="The model to use (currently ignored)")
|
model: str = Field(default="", description="The model to use (currently ignored)")
|
||||||
control: Union[ControlField, list[ControlField]] = Field(default=None, description="The control to use")
|
control: Union[ControlField, list[ControlField]] = Field(default=None, description="The control to use")
|
||||||
@ -366,7 +366,7 @@ class LatentsToLatentsInvocation(TextToLatentsInvocation):
|
|||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
latents: Optional[LatentsField] = Field(description="The latents to use as a base image")
|
latents: Optional[LatentsField] = Field(description="The latents to use as a base image")
|
||||||
strength: float = Field(default=0.5, description="The strength of the latents to use")
|
strength: float = Field(default=0.7, ge=0, le=1, description="The strength of the latents to use")
|
||||||
|
|
||||||
# Schema customisation
|
# Schema customisation
|
||||||
class Config(InvocationConfig):
|
class Config(InvocationConfig):
|
||||||
|
@ -75,10 +75,10 @@ class AddsMaskLatents:
|
|||||||
initial_image_latents: torch.Tensor
|
initial_image_latents: torch.Tensor
|
||||||
|
|
||||||
def __call__(
|
def __call__(
|
||||||
self, latents: torch.Tensor, t: torch.Tensor, text_embeddings: torch.Tensor
|
self, latents: torch.Tensor, t: torch.Tensor, text_embeddings: torch.Tensor, **kwargs,
|
||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
model_input = self.add_mask_channels(latents)
|
model_input = self.add_mask_channels(latents)
|
||||||
return self.forward(model_input, t, text_embeddings)
|
return self.forward(model_input, t, text_embeddings, **kwargs)
|
||||||
|
|
||||||
def add_mask_channels(self, latents):
|
def add_mask_channels(self, latents):
|
||||||
batch_size = latents.size(0)
|
batch_size = latents.size(0)
|
||||||
|
@ -101,7 +101,8 @@
|
|||||||
"serialize-error": "^11.0.0",
|
"serialize-error": "^11.0.0",
|
||||||
"socket.io-client": "^4.6.0",
|
"socket.io-client": "^4.6.0",
|
||||||
"use-image": "^1.1.0",
|
"use-image": "^1.1.0",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0",
|
||||||
|
"zod": "^3.21.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@chakra-ui/cli": "^2.4.0",
|
"@chakra-ui/cli": "^2.4.0",
|
||||||
|
@ -454,6 +454,8 @@
|
|||||||
"height": "Height",
|
"height": "Height",
|
||||||
"scheduler": "Scheduler",
|
"scheduler": "Scheduler",
|
||||||
"seed": "Seed",
|
"seed": "Seed",
|
||||||
|
"boundingBoxWidth": "Bounding Box Width",
|
||||||
|
"boundingBoxHeight": "Bounding Box Height",
|
||||||
"imageToImage": "Image to Image",
|
"imageToImage": "Image to Image",
|
||||||
"randomizeSeed": "Randomize Seed",
|
"randomizeSeed": "Randomize Seed",
|
||||||
"shuffle": "Shuffle Seed",
|
"shuffle": "Shuffle Seed",
|
||||||
@ -566,6 +568,8 @@
|
|||||||
"canvasMerged": "Canvas Merged",
|
"canvasMerged": "Canvas Merged",
|
||||||
"sentToImageToImage": "Sent To Image To Image",
|
"sentToImageToImage": "Sent To Image To Image",
|
||||||
"sentToUnifiedCanvas": "Sent to Unified Canvas",
|
"sentToUnifiedCanvas": "Sent to Unified Canvas",
|
||||||
|
"parameterSet": "Parameter set",
|
||||||
|
"parameterNotSet": "Parameter not set",
|
||||||
"parametersSet": "Parameters Set",
|
"parametersSet": "Parameters Set",
|
||||||
"parametersNotSet": "Parameters Not Set",
|
"parametersNotSet": "Parameters Not Set",
|
||||||
"parametersNotSetDesc": "No metadata found for this image.",
|
"parametersNotSetDesc": "No metadata found for this image.",
|
||||||
|
@ -1,27 +1,26 @@
|
|||||||
import ImageUploader from 'common/components/ImageUploader';
|
|
||||||
import SiteHeader from 'features/system/components/SiteHeader';
|
|
||||||
import ProgressBar from 'features/system/components/ProgressBar';
|
|
||||||
import InvokeTabs from 'features/ui/components/InvokeTabs';
|
|
||||||
import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton';
|
|
||||||
import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons';
|
|
||||||
import { Box, Flex, Grid, Portal } from '@chakra-ui/react';
|
import { Box, Flex, Grid, Portal } from '@chakra-ui/react';
|
||||||
import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants';
|
import { useLogger } from 'app/logging/useLogger';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { PartialAppConfig } from 'app/types/invokeai';
|
||||||
|
import ImageUploader from 'common/components/ImageUploader';
|
||||||
|
import Loading from 'common/components/Loading/Loading';
|
||||||
import GalleryDrawer from 'features/gallery/components/GalleryPanel';
|
import GalleryDrawer from 'features/gallery/components/GalleryPanel';
|
||||||
import Lightbox from 'features/lightbox/components/Lightbox';
|
import Lightbox from 'features/lightbox/components/Lightbox';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import SiteHeader from 'features/system/components/SiteHeader';
|
||||||
import { memo, ReactNode, useCallback, useEffect, useState } from 'react';
|
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
|
||||||
import Loading from 'common/components/Loading/Loading';
|
|
||||||
import { useIsApplicationReady } from 'features/system/hooks/useIsApplicationReady';
|
|
||||||
import { PartialAppConfig } from 'app/types/invokeai';
|
|
||||||
import { configChanged } from 'features/system/store/configSlice';
|
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { useLogger } from 'app/logging/useLogger';
|
import { useIsApplicationReady } from 'features/system/hooks/useIsApplicationReady';
|
||||||
import ParametersDrawer from 'features/ui/components/ParametersDrawer';
|
import { configChanged } from 'features/system/store/configSlice';
|
||||||
import { languageSelector } from 'features/system/store/systemSelectors';
|
import { languageSelector } from 'features/system/store/systemSelectors';
|
||||||
|
import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton';
|
||||||
|
import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons';
|
||||||
|
import InvokeTabs from 'features/ui/components/InvokeTabs';
|
||||||
|
import ParametersDrawer from 'features/ui/components/ParametersDrawer';
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import i18n from 'i18n';
|
import i18n from 'i18n';
|
||||||
import Toaster from './Toaster';
|
import { ReactNode, memo, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants';
|
||||||
import GlobalHotkeys from './GlobalHotkeys';
|
import GlobalHotkeys from './GlobalHotkeys';
|
||||||
|
import Toaster from './Toaster';
|
||||||
|
|
||||||
const DEFAULT_CONFIG = {};
|
const DEFAULT_CONFIG = {};
|
||||||
|
|
||||||
@ -76,7 +75,6 @@ const App = ({
|
|||||||
<Grid w="100vw" h="100vh" position="relative" overflow="hidden">
|
<Grid w="100vw" h="100vh" position="relative" overflow="hidden">
|
||||||
{isLightboxEnabled && <Lightbox />}
|
{isLightboxEnabled && <Lightbox />}
|
||||||
<ImageUploader>
|
<ImageUploader>
|
||||||
<ProgressBar />
|
|
||||||
<Grid
|
<Grid
|
||||||
gap={4}
|
gap={4}
|
||||||
p={4}
|
p={4}
|
||||||
|
@ -21,25 +21,11 @@ export const SCHEDULERS = [
|
|||||||
|
|
||||||
export type Scheduler = (typeof SCHEDULERS)[number];
|
export type Scheduler = (typeof SCHEDULERS)[number];
|
||||||
|
|
||||||
export const isScheduler = (x: string): x is Scheduler =>
|
|
||||||
SCHEDULERS.includes(x as Scheduler);
|
|
||||||
|
|
||||||
// Valid image widths
|
|
||||||
export const WIDTHS: Array<number> = Array.from(Array(64)).map(
|
|
||||||
(_x, i) => (i + 1) * 64
|
|
||||||
);
|
|
||||||
|
|
||||||
// Valid image heights
|
|
||||||
export const HEIGHTS: Array<number> = Array.from(Array(64)).map(
|
|
||||||
(_x, i) => (i + 1) * 64
|
|
||||||
);
|
|
||||||
|
|
||||||
// Valid upscaling levels
|
// Valid upscaling levels
|
||||||
export const UPSCALING_LEVELS: Array<{ key: string; value: number }> = [
|
export const UPSCALING_LEVELS: Array<{ key: string; value: number }> = [
|
||||||
{ key: '2x', value: 2 },
|
{ key: '2x', value: 2 },
|
||||||
{ key: '4x', value: 4 },
|
{ key: '4x', value: 4 },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const NUMPY_RAND_MIN = 0;
|
export const NUMPY_RAND_MIN = 0;
|
||||||
|
|
||||||
export const NUMPY_RAND_MAX = 2147483647;
|
export const NUMPY_RAND_MAX = 2147483647;
|
||||||
|
@ -1,316 +1,82 @@
|
|||||||
/**
|
|
||||||
* Types for images, the things they are made of, and the things
|
|
||||||
* they make up.
|
|
||||||
*
|
|
||||||
* Generated images are txt2img and img2img images. They may have
|
|
||||||
* had additional postprocessing done on them when they were first
|
|
||||||
* generated.
|
|
||||||
*
|
|
||||||
* Postprocessed images are images which were not generated here
|
|
||||||
* but only postprocessed by the app. They only get postprocessing
|
|
||||||
* metadata and have a different image type, e.g. 'esrgan' or
|
|
||||||
* 'gfpgan'.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { SelectedImage } from 'features/parameters/store/actions';
|
|
||||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||||
import { IRect } from 'konva/lib/types';
|
|
||||||
import { ImageResponseMetadata, ResourceOrigin } from 'services/api';
|
|
||||||
import { O } from 'ts-toolbelt';
|
import { O } from 'ts-toolbelt';
|
||||||
|
|
||||||
/**
|
// These are old types from the model management UI
|
||||||
* TODO:
|
|
||||||
* Once an image has been generated, if it is postprocessed again,
|
|
||||||
* additional postprocessing steps are added to its postprocessing
|
|
||||||
* array.
|
|
||||||
*
|
|
||||||
* TODO: Better documentation of types.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export type PromptItem = {
|
// export type ModelStatus = 'active' | 'cached' | 'not loaded';
|
||||||
prompt: string;
|
|
||||||
weight: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TECHDEBT: We need to retain compatibility with plain prompt strings and the structure Prompt type
|
// export type Model = {
|
||||||
export type Prompt = Array<PromptItem> | string;
|
// status: ModelStatus;
|
||||||
|
// description: string;
|
||||||
export type SeedWeightPair = {
|
// weights: string;
|
||||||
seed: number;
|
// config?: string;
|
||||||
weight: number;
|
// vae?: string;
|
||||||
};
|
// width?: number;
|
||||||
|
// height?: number;
|
||||||
export type SeedWeights = Array<SeedWeightPair>;
|
// default?: boolean;
|
||||||
|
// format?: string;
|
||||||
// All generated images contain these metadata.
|
|
||||||
export type CommonGeneratedImageMetadata = {
|
|
||||||
postprocessing: null | Array<ESRGANMetadata | FacetoolMetadata>;
|
|
||||||
sampler:
|
|
||||||
| 'ddim'
|
|
||||||
| 'ddpm'
|
|
||||||
| 'deis'
|
|
||||||
| 'lms'
|
|
||||||
| 'pndm'
|
|
||||||
| 'heun'
|
|
||||||
| 'heun_k'
|
|
||||||
| 'euler'
|
|
||||||
| 'euler_k'
|
|
||||||
| 'euler_a'
|
|
||||||
| 'kdpm_2'
|
|
||||||
| 'kdpm_2_a'
|
|
||||||
| 'dpmpp_2s'
|
|
||||||
| 'dpmpp_2m'
|
|
||||||
| 'dpmpp_2m_k'
|
|
||||||
| 'unipc';
|
|
||||||
prompt: Prompt;
|
|
||||||
seed: number;
|
|
||||||
variations: SeedWeights;
|
|
||||||
steps: number;
|
|
||||||
cfg_scale: number;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
seamless: boolean;
|
|
||||||
hires_fix: boolean;
|
|
||||||
extra: null | Record<string, never>; // Pending development of RFC #266
|
|
||||||
};
|
|
||||||
|
|
||||||
// txt2img and img2img images have some unique attributes.
|
|
||||||
export type Txt2ImgMetadata = CommonGeneratedImageMetadata & {
|
|
||||||
type: 'txt2img';
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Img2ImgMetadata = CommonGeneratedImageMetadata & {
|
|
||||||
type: 'img2img';
|
|
||||||
orig_hash: string;
|
|
||||||
strength: number;
|
|
||||||
fit: boolean;
|
|
||||||
init_image_path: string;
|
|
||||||
mask_image_path?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Superset of generated image metadata types.
|
|
||||||
export type GeneratedImageMetadata = Txt2ImgMetadata | Img2ImgMetadata;
|
|
||||||
|
|
||||||
// All post processed images contain these metadata.
|
|
||||||
export type CommonPostProcessedImageMetadata = {
|
|
||||||
orig_path: string;
|
|
||||||
orig_hash: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// esrgan and gfpgan images have some unique attributes.
|
|
||||||
export type ESRGANMetadata = CommonPostProcessedImageMetadata & {
|
|
||||||
type: 'esrgan';
|
|
||||||
scale: 2 | 4;
|
|
||||||
strength: number;
|
|
||||||
denoise_str: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type FacetoolMetadata = CommonPostProcessedImageMetadata & {
|
|
||||||
type: 'gfpgan' | 'codeformer';
|
|
||||||
strength: number;
|
|
||||||
fidelity?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Superset of all postprocessed image metadata types..
|
|
||||||
export type PostProcessedImageMetadata = ESRGANMetadata | FacetoolMetadata;
|
|
||||||
|
|
||||||
// Metadata includes the system config and image metadata.
|
|
||||||
// export type Metadata = SystemGenerationMetadata & {
|
|
||||||
// image: GeneratedImageMetadata | PostProcessedImageMetadata;
|
|
||||||
// };
|
// };
|
||||||
|
|
||||||
/**
|
// export type DiffusersModel = {
|
||||||
* ResultImage
|
// status: ModelStatus;
|
||||||
*/
|
// description: string;
|
||||||
// export ty`pe Image = {
|
// repo_id?: string;
|
||||||
|
// path?: string;
|
||||||
|
// vae?: {
|
||||||
|
// repo_id?: string;
|
||||||
|
// path?: string;
|
||||||
|
// };
|
||||||
|
// format?: string;
|
||||||
|
// default?: boolean;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export type ModelList = Record<string, Model & DiffusersModel>;
|
||||||
|
|
||||||
|
// export type FoundModel = {
|
||||||
// name: string;
|
// name: string;
|
||||||
// type: image_origin;
|
// location: string;
|
||||||
// url: string;
|
|
||||||
// thumbnail: string;
|
|
||||||
// metadata: ImageResponseMetadata;
|
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// export const isInvokeAIImage = (obj: Image | SelectedImage): obj is Image => {
|
// export type InvokeModelConfigProps = {
|
||||||
// if ('url' in obj && 'thumbnail' in obj) {
|
// name: string | undefined;
|
||||||
// return true;
|
// description: string | undefined;
|
||||||
// }
|
// config: string | undefined;
|
||||||
|
// weights: string | undefined;
|
||||||
// return false;
|
// vae: string | undefined;
|
||||||
|
// width: number | undefined;
|
||||||
|
// height: number | undefined;
|
||||||
|
// default: boolean | undefined;
|
||||||
|
// format: string | undefined;
|
||||||
// };
|
// };
|
||||||
|
|
||||||
/**
|
// export type InvokeDiffusersModelConfigProps = {
|
||||||
* Types related to the system status.
|
// name: string | undefined;
|
||||||
*/
|
// description: string | undefined;
|
||||||
|
// repo_id: string | undefined;
|
||||||
// // This represents the processing status of the backend.
|
// path: string | undefined;
|
||||||
// export type SystemStatus = {
|
// default: boolean | undefined;
|
||||||
// isProcessing: boolean;
|
// format: string | undefined;
|
||||||
// currentStep: number;
|
// vae: {
|
||||||
// totalSteps: number;
|
// repo_id: string | undefined;
|
||||||
// currentIteration: number;
|
// path: string | undefined;
|
||||||
// totalIterations: number;
|
// };
|
||||||
// currentStatus: string;
|
|
||||||
// currentStatusHasSteps: boolean;
|
|
||||||
// hasError: boolean;
|
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// export type SystemGenerationMetadata = {
|
// export type InvokeModelConversionProps = {
|
||||||
// model: string;
|
// model_name: string;
|
||||||
// model_weights?: string;
|
// save_location: string;
|
||||||
// model_id?: string;
|
// custom_location: string | null;
|
||||||
// model_hash: string;
|
|
||||||
// app_id: string;
|
|
||||||
// app_version: string;
|
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// export type SystemConfig = SystemGenerationMetadata & {
|
// export type InvokeModelMergingProps = {
|
||||||
// model_list: ModelList;
|
// models_to_merge: string[];
|
||||||
// infill_methods: string[];
|
// alpha: number;
|
||||||
|
// interp: 'weighted_sum' | 'sigmoid' | 'inv_sigmoid' | 'add_difference';
|
||||||
|
// force: boolean;
|
||||||
|
// merged_model_name: string;
|
||||||
|
// model_merge_save_path: string | null;
|
||||||
// };
|
// };
|
||||||
|
|
||||||
export type ModelStatus = 'active' | 'cached' | 'not loaded';
|
|
||||||
|
|
||||||
export type Model = {
|
|
||||||
status: ModelStatus;
|
|
||||||
description: string;
|
|
||||||
weights: string;
|
|
||||||
config?: string;
|
|
||||||
vae?: string;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
default?: boolean;
|
|
||||||
format?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DiffusersModel = {
|
|
||||||
status: ModelStatus;
|
|
||||||
description: string;
|
|
||||||
repo_id?: string;
|
|
||||||
path?: string;
|
|
||||||
vae?: {
|
|
||||||
repo_id?: string;
|
|
||||||
path?: string;
|
|
||||||
};
|
|
||||||
format?: string;
|
|
||||||
default?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ModelList = Record<string, Model & DiffusersModel>;
|
|
||||||
|
|
||||||
export type FoundModel = {
|
|
||||||
name: string;
|
|
||||||
location: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InvokeModelConfigProps = {
|
|
||||||
name: string | undefined;
|
|
||||||
description: string | undefined;
|
|
||||||
config: string | undefined;
|
|
||||||
weights: string | undefined;
|
|
||||||
vae: string | undefined;
|
|
||||||
width: number | undefined;
|
|
||||||
height: number | undefined;
|
|
||||||
default: boolean | undefined;
|
|
||||||
format: string | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InvokeDiffusersModelConfigProps = {
|
|
||||||
name: string | undefined;
|
|
||||||
description: string | undefined;
|
|
||||||
repo_id: string | undefined;
|
|
||||||
path: string | undefined;
|
|
||||||
default: boolean | undefined;
|
|
||||||
format: string | undefined;
|
|
||||||
vae: {
|
|
||||||
repo_id: string | undefined;
|
|
||||||
path: string | undefined;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InvokeModelConversionProps = {
|
|
||||||
model_name: string;
|
|
||||||
save_location: string;
|
|
||||||
custom_location: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InvokeModelMergingProps = {
|
|
||||||
models_to_merge: string[];
|
|
||||||
alpha: number;
|
|
||||||
interp: 'weighted_sum' | 'sigmoid' | 'inv_sigmoid' | 'add_difference';
|
|
||||||
force: boolean;
|
|
||||||
merged_model_name: string;
|
|
||||||
model_merge_save_path: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* These types type data received from the server via socketio.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export type ModelChangeResponse = {
|
|
||||||
model_name: string;
|
|
||||||
model_list: ModelList;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ModelConvertedResponse = {
|
|
||||||
converted_model_name: string;
|
|
||||||
model_list: ModelList;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ModelsMergedResponse = {
|
|
||||||
merged_models: string[];
|
|
||||||
merged_model_name: string;
|
|
||||||
model_list: ModelList;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ModelAddedResponse = {
|
|
||||||
new_model_name: string;
|
|
||||||
model_list: ModelList;
|
|
||||||
update: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ModelDeletedResponse = {
|
|
||||||
deleted_model_name: string;
|
|
||||||
model_list: ModelList;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type FoundModelResponse = {
|
|
||||||
search_folder: string;
|
|
||||||
found_models: FoundModel[];
|
|
||||||
};
|
|
||||||
|
|
||||||
// export type SystemStatusResponse = SystemStatus;
|
|
||||||
|
|
||||||
// export type SystemConfigResponse = SystemConfig;
|
|
||||||
|
|
||||||
export type ImageResultResponse = Omit<Image, 'uuid'> & {
|
|
||||||
boundingBox?: IRect;
|
|
||||||
generationMode: InvokeTabName;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ImageUploadResponse = {
|
|
||||||
// image: Omit<Image, 'uuid' | 'metadata' | 'category'>;
|
|
||||||
url: string;
|
|
||||||
mtime: number;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
thumbnail: string;
|
|
||||||
// bbox: [number, number, number, number];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ErrorResponse = {
|
|
||||||
message: string;
|
|
||||||
additionalData?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ImageUrlResponse = {
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UploadOutpaintingMergeImagePayload = {
|
|
||||||
dataURL: string;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A disable-able application feature
|
* A disable-able application feature
|
||||||
*/
|
*/
|
||||||
@ -322,7 +88,8 @@ export type AppFeature =
|
|||||||
| 'githubLink'
|
| 'githubLink'
|
||||||
| 'discordLink'
|
| 'discordLink'
|
||||||
| 'bugLink'
|
| 'bugLink'
|
||||||
| 'localization';
|
| 'localization'
|
||||||
|
| 'consoleLogging';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A disable-able Stable Diffusion feature
|
* A disable-able Stable Diffusion feature
|
||||||
@ -351,6 +118,7 @@ export type AppConfig = {
|
|||||||
disabledSDFeatures: SDFeature[];
|
disabledSDFeatures: SDFeature[];
|
||||||
canRestoreDeletedImagesFromBin: boolean;
|
canRestoreDeletedImagesFromBin: boolean;
|
||||||
sd: {
|
sd: {
|
||||||
|
defaultModel?: string;
|
||||||
iterations: {
|
iterations: {
|
||||||
initial: number;
|
initial: number;
|
||||||
min: number;
|
min: number;
|
||||||
|
@ -21,9 +21,12 @@ import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
|||||||
|
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
export type ItemTooltips = { [key: string]: string };
|
||||||
|
|
||||||
type IAICustomSelectProps = {
|
type IAICustomSelectProps = {
|
||||||
label?: string;
|
label?: string;
|
||||||
items: string[];
|
items: string[];
|
||||||
|
itemTooltips?: ItemTooltips;
|
||||||
selectedItem: string;
|
selectedItem: string;
|
||||||
setSelectedItem: (v: string | null | undefined) => void;
|
setSelectedItem: (v: string | null | undefined) => void;
|
||||||
withCheckIcon?: boolean;
|
withCheckIcon?: boolean;
|
||||||
@ -37,6 +40,7 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
|
|||||||
const {
|
const {
|
||||||
label,
|
label,
|
||||||
items,
|
items,
|
||||||
|
itemTooltips,
|
||||||
setSelectedItem,
|
setSelectedItem,
|
||||||
selectedItem,
|
selectedItem,
|
||||||
withCheckIcon,
|
withCheckIcon,
|
||||||
@ -118,6 +122,13 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
|
|||||||
>
|
>
|
||||||
<OverlayScrollbarsComponent>
|
<OverlayScrollbarsComponent>
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
|
<Tooltip
|
||||||
|
isDisabled={!itemTooltips}
|
||||||
|
key={`${item}${index}`}
|
||||||
|
label={itemTooltips?.[item]}
|
||||||
|
hasArrow
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
<ListItem
|
<ListItem
|
||||||
sx={{
|
sx={{
|
||||||
bg: highlightedIndex === index ? 'base.700' : undefined,
|
bg: highlightedIndex === index ? 'base.700' : undefined,
|
||||||
@ -160,6 +171,7 @@ const IAICustomSelect = (props: IAICustomSelectProps) => {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
</OverlayScrollbarsComponent>
|
</OverlayScrollbarsComponent>
|
||||||
</List>
|
</List>
|
||||||
|
@ -1,119 +0,0 @@
|
|||||||
/**
|
|
||||||
* PARTIAL ZOD IMPLEMENTATION
|
|
||||||
*
|
|
||||||
* doesn't work well bc like most validators, zod is not built to skip invalid values.
|
|
||||||
* it mostly works but just seems clearer and simpler to manually parse for now.
|
|
||||||
*
|
|
||||||
* in the future it would be really nice if we could use zod for some things:
|
|
||||||
* - zodios (axios + zod): https://github.com/ecyrbe/zodios
|
|
||||||
* - openapi to zodios: https://github.com/astahmer/openapi-zod-client
|
|
||||||
*/
|
|
||||||
|
|
||||||
// import { z } from 'zod';
|
|
||||||
|
|
||||||
// const zMetadataStringField = z.string();
|
|
||||||
// export type MetadataStringField = z.infer<typeof zMetadataStringField>;
|
|
||||||
|
|
||||||
// const zMetadataIntegerField = z.number().int();
|
|
||||||
// export type MetadataIntegerField = z.infer<typeof zMetadataIntegerField>;
|
|
||||||
|
|
||||||
// const zMetadataFloatField = z.number();
|
|
||||||
// export type MetadataFloatField = z.infer<typeof zMetadataFloatField>;
|
|
||||||
|
|
||||||
// const zMetadataBooleanField = z.boolean();
|
|
||||||
// export type MetadataBooleanField = z.infer<typeof zMetadataBooleanField>;
|
|
||||||
|
|
||||||
// const zMetadataImageField = z.object({
|
|
||||||
// image_type: z.union([
|
|
||||||
// z.literal('results'),
|
|
||||||
// z.literal('uploads'),
|
|
||||||
// z.literal('intermediates'),
|
|
||||||
// ]),
|
|
||||||
// image_name: z.string().min(1),
|
|
||||||
// });
|
|
||||||
// export type MetadataImageField = z.infer<typeof zMetadataImageField>;
|
|
||||||
|
|
||||||
// const zMetadataLatentsField = z.object({
|
|
||||||
// latents_name: z.string().min(1),
|
|
||||||
// });
|
|
||||||
// export type MetadataLatentsField = z.infer<typeof zMetadataLatentsField>;
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * zod Schema for any node field. Use a `transform()` to manually parse, skipping invalid values.
|
|
||||||
// */
|
|
||||||
// const zAnyMetadataField = z.any().transform((val, ctx) => {
|
|
||||||
// // Grab the field name from the path
|
|
||||||
// const fieldName = String(ctx.path[ctx.path.length - 1]);
|
|
||||||
|
|
||||||
// // `id` and `type` must be strings if they exist
|
|
||||||
// if (['id', 'type'].includes(fieldName)) {
|
|
||||||
// const reservedStringPropertyResult = zMetadataStringField.safeParse(val);
|
|
||||||
// if (reservedStringPropertyResult.success) {
|
|
||||||
// return reservedStringPropertyResult.data;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Parse the rest of the fields, only returning the data if the parsing is successful
|
|
||||||
|
|
||||||
// const stringFieldResult = zMetadataStringField.safeParse(val);
|
|
||||||
// if (stringFieldResult.success) {
|
|
||||||
// return stringFieldResult.data;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const integerFieldResult = zMetadataIntegerField.safeParse(val);
|
|
||||||
// if (integerFieldResult.success) {
|
|
||||||
// return integerFieldResult.data;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const floatFieldResult = zMetadataFloatField.safeParse(val);
|
|
||||||
// if (floatFieldResult.success) {
|
|
||||||
// return floatFieldResult.data;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const booleanFieldResult = zMetadataBooleanField.safeParse(val);
|
|
||||||
// if (booleanFieldResult.success) {
|
|
||||||
// return booleanFieldResult.data;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const imageFieldResult = zMetadataImageField.safeParse(val);
|
|
||||||
// if (imageFieldResult.success) {
|
|
||||||
// return imageFieldResult.data;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const latentsFieldResult = zMetadataImageField.safeParse(val);
|
|
||||||
// if (latentsFieldResult.success) {
|
|
||||||
// return latentsFieldResult.data;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * The node metadata schema.
|
|
||||||
// */
|
|
||||||
// const zNodeMetadata = z.object({
|
|
||||||
// session_id: z.string().min(1).optional(),
|
|
||||||
// node: z.record(z.string().min(1), zAnyMetadataField).optional(),
|
|
||||||
// });
|
|
||||||
|
|
||||||
// export type NodeMetadata = z.infer<typeof zNodeMetadata>;
|
|
||||||
|
|
||||||
// const zMetadata = z.object({
|
|
||||||
// invokeai: zNodeMetadata.optional(),
|
|
||||||
// 'sd-metadata': z.record(z.string().min(1), z.any()).optional(),
|
|
||||||
// });
|
|
||||||
// export type Metadata = z.infer<typeof zMetadata>;
|
|
||||||
|
|
||||||
// export const parseMetadata = (
|
|
||||||
// metadata: Record<string, any>
|
|
||||||
// ): Metadata | undefined => {
|
|
||||||
// const result = zMetadata.safeParse(metadata);
|
|
||||||
// if (!result.success) {
|
|
||||||
// console.log(result.error.issues);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return result.data;
|
|
||||||
// };
|
|
||||||
|
|
||||||
export default {};
|
|
@ -29,6 +29,7 @@ import {
|
|||||||
isCanvasMaskLine,
|
isCanvasMaskLine,
|
||||||
} from './canvasTypes';
|
} from './canvasTypes';
|
||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
|
import { sessionCanceled } from 'services/thunks/session';
|
||||||
|
|
||||||
export const initialLayerState: CanvasLayerState = {
|
export const initialLayerState: CanvasLayerState = {
|
||||||
objects: [],
|
objects: [],
|
||||||
@ -844,6 +845,13 @@ export const canvasSlice = createSlice({
|
|||||||
state.isTransformingBoundingBox = false;
|
state.isTransformingBoundingBox = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(sessionCanceled.pending, (state) => {
|
||||||
|
if (!state.layerState.stagingArea.images.length) {
|
||||||
|
state.layerState.stagingArea = initialLayerState.stagingArea;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
|
@ -9,7 +9,8 @@ import { IRect } from 'konva/lib/types';
|
|||||||
*/
|
*/
|
||||||
const createMaskStage = async (
|
const createMaskStage = async (
|
||||||
lines: CanvasMaskLine[],
|
lines: CanvasMaskLine[],
|
||||||
boundingBox: IRect
|
boundingBox: IRect,
|
||||||
|
shouldInvertMask: boolean
|
||||||
): Promise<Konva.Stage> => {
|
): Promise<Konva.Stage> => {
|
||||||
// create an offscreen canvas and add the mask to it
|
// create an offscreen canvas and add the mask to it
|
||||||
const { width, height } = boundingBox;
|
const { width, height } = boundingBox;
|
||||||
@ -29,7 +30,7 @@ const createMaskStage = async (
|
|||||||
baseLayer.add(
|
baseLayer.add(
|
||||||
new Konva.Rect({
|
new Konva.Rect({
|
||||||
...boundingBox,
|
...boundingBox,
|
||||||
fill: 'white',
|
fill: shouldInvertMask ? 'black' : 'white',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ const createMaskStage = async (
|
|||||||
maskLayer.add(
|
maskLayer.add(
|
||||||
new Konva.Line({
|
new Konva.Line({
|
||||||
points: line.points,
|
points: line.points,
|
||||||
stroke: 'black',
|
stroke: shouldInvertMask ? 'white' : 'black',
|
||||||
strokeWidth: line.strokeWidth * 2,
|
strokeWidth: line.strokeWidth * 2,
|
||||||
tension: 0,
|
tension: 0,
|
||||||
lineCap: 'round',
|
lineCap: 'round',
|
||||||
|
@ -25,6 +25,7 @@ export const getCanvasData = async (state: RootState) => {
|
|||||||
boundingBoxCoordinates,
|
boundingBoxCoordinates,
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
isMaskEnabled,
|
isMaskEnabled,
|
||||||
|
shouldPreserveMaskedArea,
|
||||||
} = state.canvas;
|
} = state.canvas;
|
||||||
|
|
||||||
const boundingBox = {
|
const boundingBox = {
|
||||||
@ -58,7 +59,8 @@ export const getCanvasData = async (state: RootState) => {
|
|||||||
// For the mask layer, use the normal boundingBox
|
// For the mask layer, use the normal boundingBox
|
||||||
const maskStage = await createMaskStage(
|
const maskStage = await createMaskStage(
|
||||||
isMaskEnabled ? objects.filter(isCanvasMaskLine) : [], // only include mask lines, and only if mask is enabled
|
isMaskEnabled ? objects.filter(isCanvasMaskLine) : [], // only include mask lines, and only if mask is enabled
|
||||||
boundingBox
|
boundingBox,
|
||||||
|
shouldPreserveMaskedArea
|
||||||
);
|
);
|
||||||
const maskBlob = await konvaNodeToBlob(maskStage, boundingBox);
|
const maskBlob = await konvaNodeToBlob(maskStage, boundingBox);
|
||||||
const maskImageData = await konvaNodeToImageData(maskStage, boundingBox);
|
const maskImageData = await konvaNodeToImageData(maskStage, boundingBox);
|
||||||
|
@ -49,7 +49,7 @@ import { useCallback } from 'react';
|
|||||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { useParameters } from 'features/parameters/hooks/useParameters';
|
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
import {
|
import {
|
||||||
requestedImageDeletion,
|
requestedImageDeletion,
|
||||||
@ -58,7 +58,6 @@ import {
|
|||||||
} from '../store/actions';
|
} from '../store/actions';
|
||||||
import FaceRestoreSettings from 'features/parameters/components/Parameters/FaceRestore/FaceRestoreSettings';
|
import FaceRestoreSettings from 'features/parameters/components/Parameters/FaceRestore/FaceRestoreSettings';
|
||||||
import UpscaleSettings from 'features/parameters/components/Parameters/Upscale/UpscaleSettings';
|
import UpscaleSettings from 'features/parameters/components/Parameters/Upscale/UpscaleSettings';
|
||||||
import { allParametersSet } from 'features/parameters/store/generationSlice';
|
|
||||||
import DeleteImageButton from './ImageActionButtons/DeleteImageButton';
|
import DeleteImageButton from './ImageActionButtons/DeleteImageButton';
|
||||||
import { useAppToaster } from 'app/components/Toaster';
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
@ -165,7 +164,8 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
const toaster = useAppToaster();
|
const toaster = useAppToaster();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { recallPrompt, recallSeed, recallAllParameters } = useParameters();
|
const { recallBothPrompts, recallSeed, recallAllParameters } =
|
||||||
|
useRecallParameters();
|
||||||
|
|
||||||
// const handleCopyImage = useCallback(async () => {
|
// const handleCopyImage = useCallback(async () => {
|
||||||
// if (!image?.url) {
|
// if (!image?.url) {
|
||||||
@ -250,11 +250,11 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
useHotkeys('s', handleUseSeed, [image]);
|
useHotkeys('s', handleUseSeed, [image]);
|
||||||
|
|
||||||
const handleUsePrompt = useCallback(() => {
|
const handleUsePrompt = useCallback(() => {
|
||||||
recallPrompt(
|
recallBothPrompts(
|
||||||
image?.metadata?.positive_conditioning,
|
image?.metadata?.positive_conditioning,
|
||||||
image?.metadata?.negative_conditioning
|
image?.metadata?.negative_conditioning
|
||||||
);
|
);
|
||||||
}, [image, recallPrompt]);
|
}, [image, recallBothPrompts]);
|
||||||
|
|
||||||
useHotkeys('p', handleUsePrompt, [image]);
|
useHotkeys('p', handleUsePrompt, [image]);
|
||||||
|
|
||||||
@ -461,7 +461,11 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
{t('parameters.copyImageToLink')}
|
{t('parameters.copyImageToLink')}
|
||||||
</IAIButton>
|
</IAIButton>
|
||||||
|
|
||||||
<Link download={true} href={getUrl(image?.image_url ?? '')}>
|
<Link
|
||||||
|
download={true}
|
||||||
|
href={getUrl(image?.image_url ?? '')}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
<IAIButton leftIcon={<FaDownload />} size="sm" w="100%">
|
<IAIButton leftIcon={<FaDownload />} size="sm" w="100%">
|
||||||
{t('parameters.downloadImage')}
|
{t('parameters.downloadImage')}
|
||||||
</IAIButton>
|
</IAIButton>
|
||||||
|
@ -30,7 +30,7 @@ import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
|
|||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { useParameters } from 'features/parameters/hooks/useParameters';
|
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
import {
|
import {
|
||||||
requestedImageDeletion,
|
requestedImageDeletion,
|
||||||
@ -114,8 +114,8 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
const isLightboxEnabled = useFeatureStatus('lightbox').isFeatureEnabled;
|
const isLightboxEnabled = useFeatureStatus('lightbox').isFeatureEnabled;
|
||||||
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
|
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
|
||||||
|
|
||||||
const { recallSeed, recallPrompt, recallInitialImage, recallAllParameters } =
|
const { recallBothPrompts, recallSeed, recallAllParameters } =
|
||||||
useParameters();
|
useRecallParameters();
|
||||||
|
|
||||||
const handleMouseOver = () => setIsHovered(true);
|
const handleMouseOver = () => setIsHovered(true);
|
||||||
const handleMouseOut = () => setIsHovered(false);
|
const handleMouseOut = () => setIsHovered(false);
|
||||||
@ -154,11 +154,15 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
|
|
||||||
// Recall parameters handlers
|
// Recall parameters handlers
|
||||||
const handleRecallPrompt = useCallback(() => {
|
const handleRecallPrompt = useCallback(() => {
|
||||||
recallPrompt(
|
recallBothPrompts(
|
||||||
image.metadata?.positive_conditioning,
|
image.metadata?.positive_conditioning,
|
||||||
image.metadata?.negative_conditioning
|
image.metadata?.negative_conditioning
|
||||||
);
|
);
|
||||||
}, [image, recallPrompt]);
|
}, [
|
||||||
|
image.metadata?.negative_conditioning,
|
||||||
|
image.metadata?.positive_conditioning,
|
||||||
|
recallBothPrompts,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleRecallSeed = useCallback(() => {
|
const handleRecallSeed = useCallback(() => {
|
||||||
recallSeed(image.metadata?.seed);
|
recallSeed(image.metadata?.seed);
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
Checkbox,
|
|
||||||
CheckboxGroup,
|
|
||||||
Flex,
|
Flex,
|
||||||
FlexProps,
|
FlexProps,
|
||||||
Grid,
|
Grid,
|
||||||
@ -32,18 +30,13 @@ import {
|
|||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
|
import { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
|
||||||
import {
|
import { FaImage, FaServer, FaWrench } from 'react-icons/fa';
|
||||||
FaFilter,
|
|
||||||
FaImage,
|
|
||||||
FaImages,
|
|
||||||
FaServer,
|
|
||||||
FaWrench,
|
|
||||||
} from 'react-icons/fa';
|
|
||||||
import { MdPhotoLibrary } from 'react-icons/md';
|
import { MdPhotoLibrary } from 'react-icons/md';
|
||||||
import HoverableImage from './HoverableImage';
|
import HoverableImage from './HoverableImage';
|
||||||
|
|
||||||
@ -53,7 +46,6 @@ import { RootState } from 'app/store/store';
|
|||||||
import { Virtuoso, VirtuosoGrid } from 'react-virtuoso';
|
import { Virtuoso, VirtuosoGrid } from 'react-virtuoso';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { ImageCategory } from 'services/api';
|
|
||||||
import {
|
import {
|
||||||
ASSETS_CATEGORIES,
|
ASSETS_CATEGORIES,
|
||||||
IMAGE_CATEGORIES,
|
IMAGE_CATEGORIES,
|
||||||
@ -61,7 +53,6 @@ import {
|
|||||||
selectImagesAll,
|
selectImagesAll,
|
||||||
} from '../store/imagesSlice';
|
} from '../store/imagesSlice';
|
||||||
import { receivedPageOfImages } from 'services/thunks/image';
|
import { receivedPageOfImages } from 'services/thunks/image';
|
||||||
import { capitalize } from 'lodash-es';
|
|
||||||
|
|
||||||
const categorySelector = createSelector(
|
const categorySelector = createSelector(
|
||||||
[(state: RootState) => state],
|
[(state: RootState) => state],
|
||||||
@ -144,6 +135,13 @@ const ImageGalleryContent = () => {
|
|||||||
dispatch(receivedPageOfImages());
|
dispatch(receivedPageOfImages());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handleEndReached = useMemo(() => {
|
||||||
|
if (areMoreImagesAvailable && !isLoading) {
|
||||||
|
return handleLoadMoreImages;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [areMoreImagesAvailable, handleLoadMoreImages, isLoading]);
|
||||||
|
|
||||||
const handleChangeGalleryImageMinimumWidth = (v: number) => {
|
const handleChangeGalleryImageMinimumWidth = (v: number) => {
|
||||||
dispatch(setGalleryImageMinimumWidth(v));
|
dispatch(setGalleryImageMinimumWidth(v));
|
||||||
};
|
};
|
||||||
@ -172,17 +170,6 @@ const ImageGalleryContent = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleEndReached = useCallback(() => {
|
|
||||||
handleLoadMoreImages();
|
|
||||||
}, [handleLoadMoreImages]);
|
|
||||||
|
|
||||||
const handleCategoriesChanged = useCallback(
|
|
||||||
(newCategories: ImageCategory[]) => {
|
|
||||||
dispatch(imageCategoriesChanged(newCategories));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClickImagesCategory = useCallback(() => {
|
const handleClickImagesCategory = useCallback(() => {
|
||||||
dispatch(imageCategoriesChanged(IMAGE_CATEGORIES));
|
dispatch(imageCategoriesChanged(IMAGE_CATEGORIES));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
@ -31,6 +31,7 @@ import { IoArrowUndoCircleOutline } from 'react-icons/io5';
|
|||||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
import { Scheduler } from 'app/constants';
|
import { Scheduler } from 'app/constants';
|
||||||
|
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
||||||
|
|
||||||
type MetadataItemProps = {
|
type MetadataItemProps = {
|
||||||
isLink?: boolean;
|
isLink?: boolean;
|
||||||
@ -120,6 +121,21 @@ const memoEqualityCheck = (
|
|||||||
*/
|
*/
|
||||||
const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const {
|
||||||
|
recallBothPrompts,
|
||||||
|
recallPositivePrompt,
|
||||||
|
recallNegativePrompt,
|
||||||
|
recallSeed,
|
||||||
|
recallInitialImage,
|
||||||
|
recallCfgScale,
|
||||||
|
recallModel,
|
||||||
|
recallScheduler,
|
||||||
|
recallSteps,
|
||||||
|
recallWidth,
|
||||||
|
recallHeight,
|
||||||
|
recallStrength,
|
||||||
|
recallAllParameters,
|
||||||
|
} = useRecallParameters();
|
||||||
|
|
||||||
useHotkeys('esc', () => {
|
useHotkeys('esc', () => {
|
||||||
dispatch(setShouldShowImageDetails(false));
|
dispatch(setShouldShowImageDetails(false));
|
||||||
@ -166,52 +182,53 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
|||||||
{metadata.type && (
|
{metadata.type && (
|
||||||
<MetadataItem label="Invocation type" value={metadata.type} />
|
<MetadataItem label="Invocation type" value={metadata.type} />
|
||||||
)}
|
)}
|
||||||
{metadata.width && (
|
{sessionId && <MetadataItem label="Session ID" value={sessionId} />}
|
||||||
<MetadataItem
|
|
||||||
label="Width"
|
|
||||||
value={metadata.width}
|
|
||||||
onClick={() => dispatch(setWidth(Number(metadata.width)))}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{metadata.height && (
|
|
||||||
<MetadataItem
|
|
||||||
label="Height"
|
|
||||||
value={metadata.height}
|
|
||||||
onClick={() => dispatch(setHeight(Number(metadata.height)))}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{metadata.model && (
|
|
||||||
<MetadataItem label="Model" value={metadata.model} />
|
|
||||||
)}
|
|
||||||
{metadata.positive_conditioning && (
|
{metadata.positive_conditioning && (
|
||||||
<MetadataItem
|
<MetadataItem
|
||||||
label="Prompt"
|
label="Positive Prompt"
|
||||||
labelPosition="top"
|
labelPosition="top"
|
||||||
value={
|
value={metadata.positive_conditioning}
|
||||||
typeof metadata.positive_conditioning === 'string'
|
onClick={() =>
|
||||||
? metadata.positive_conditioning
|
recallPositivePrompt(metadata.positive_conditioning)
|
||||||
: promptToString(metadata.positive_conditioning)
|
|
||||||
}
|
}
|
||||||
onClick={() => setPositivePrompt(metadata.positive_conditioning!)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{metadata.negative_conditioning && (
|
{metadata.negative_conditioning && (
|
||||||
<MetadataItem
|
<MetadataItem
|
||||||
label="Prompt"
|
label="Negative Prompt"
|
||||||
labelPosition="top"
|
labelPosition="top"
|
||||||
value={
|
value={metadata.negative_conditioning}
|
||||||
typeof metadata.negative_conditioning === 'string'
|
onClick={() =>
|
||||||
? metadata.negative_conditioning
|
recallNegativePrompt(metadata.negative_conditioning)
|
||||||
: promptToString(metadata.negative_conditioning)
|
|
||||||
}
|
}
|
||||||
onClick={() => setNegativePrompt(metadata.negative_conditioning!)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{metadata.seed !== undefined && (
|
{metadata.seed !== undefined && (
|
||||||
<MetadataItem
|
<MetadataItem
|
||||||
label="Seed"
|
label="Seed"
|
||||||
value={metadata.seed}
|
value={metadata.seed}
|
||||||
onClick={() => dispatch(setSeed(Number(metadata.seed)))}
|
onClick={() => recallSeed(metadata.seed)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{metadata.model !== undefined && (
|
||||||
|
<MetadataItem
|
||||||
|
label="Model"
|
||||||
|
value={metadata.model}
|
||||||
|
onClick={() => recallModel(metadata.model)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{metadata.width && (
|
||||||
|
<MetadataItem
|
||||||
|
label="Width"
|
||||||
|
value={metadata.width}
|
||||||
|
onClick={() => recallWidth(metadata.width)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{metadata.height && (
|
||||||
|
<MetadataItem
|
||||||
|
label="Height"
|
||||||
|
value={metadata.height}
|
||||||
|
onClick={() => recallHeight(metadata.height)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* {metadata.threshold !== undefined && (
|
{/* {metadata.threshold !== undefined && (
|
||||||
@ -232,23 +249,21 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
|||||||
<MetadataItem
|
<MetadataItem
|
||||||
label="Scheduler"
|
label="Scheduler"
|
||||||
value={metadata.scheduler}
|
value={metadata.scheduler}
|
||||||
onClick={() =>
|
onClick={() => recallScheduler(metadata.scheduler)}
|
||||||
dispatch(setScheduler(metadata.scheduler as Scheduler))
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{metadata.steps && (
|
{metadata.steps && (
|
||||||
<MetadataItem
|
<MetadataItem
|
||||||
label="Steps"
|
label="Steps"
|
||||||
value={metadata.steps}
|
value={metadata.steps}
|
||||||
onClick={() => dispatch(setSteps(Number(metadata.steps)))}
|
onClick={() => recallSteps(metadata.steps)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{metadata.cfg_scale !== undefined && (
|
{metadata.cfg_scale !== undefined && (
|
||||||
<MetadataItem
|
<MetadataItem
|
||||||
label="CFG scale"
|
label="CFG scale"
|
||||||
value={metadata.cfg_scale}
|
value={metadata.cfg_scale}
|
||||||
onClick={() => dispatch(setCfgScale(Number(metadata.cfg_scale)))}
|
onClick={() => recallCfgScale(metadata.cfg_scale)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* {metadata.variations && metadata.variations.length > 0 && (
|
{/* {metadata.variations && metadata.variations.length > 0 && (
|
||||||
@ -289,9 +304,7 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
|||||||
<MetadataItem
|
<MetadataItem
|
||||||
label="Image to image strength"
|
label="Image to image strength"
|
||||||
value={metadata.strength}
|
value={metadata.strength}
|
||||||
onClick={() =>
|
onClick={() => recallStrength(metadata.strength)}
|
||||||
dispatch(setImg2imgStrength(Number(metadata.strength)))
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* {metadata.fit && (
|
{/* {metadata.fit && (
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
import { HStack } from '@chakra-ui/react';
|
import { HStack } from '@chakra-ui/react';
|
||||||
import { userInvoked } from 'app/store/actions';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { Panel } from 'reactflow';
|
import { Panel } from 'reactflow';
|
||||||
import { receivedOpenAPISchema } from 'services/thunks/schema';
|
import { receivedOpenAPISchema } from 'services/thunks/schema';
|
||||||
|
import NodeInvokeButton from '../ui/NodeInvokeButton';
|
||||||
|
|
||||||
const TopCenterPanel = () => {
|
const TopCenterPanel = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const handleInvoke = useCallback(() => {
|
|
||||||
dispatch(userInvoked('nodes'));
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
const handleReloadSchema = useCallback(() => {
|
const handleReloadSchema = useCallback(() => {
|
||||||
dispatch(receivedOpenAPISchema());
|
dispatch(receivedOpenAPISchema());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
@ -20,9 +16,7 @@ const TopCenterPanel = () => {
|
|||||||
return (
|
return (
|
||||||
<Panel position="top-center">
|
<Panel position="top-center">
|
||||||
<HStack>
|
<HStack>
|
||||||
<IAIButton colorScheme="accent" onClick={handleInvoke}>
|
<NodeInvokeButton />
|
||||||
Will it blend?
|
|
||||||
</IAIButton>
|
|
||||||
<IAIButton onClick={handleReloadSchema}>Reload Schema</IAIButton>
|
<IAIButton onClick={handleReloadSchema}>Reload Schema</IAIButton>
|
||||||
</HStack>
|
</HStack>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
import { Box } from '@chakra-ui/react';
|
||||||
|
import { readinessSelector } from 'app/selectors/readinessSelector';
|
||||||
|
import { userInvoked } from 'app/store/actions';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import IAIButton, { IAIButtonProps } from 'common/components/IAIButton';
|
||||||
|
import IAIIconButton, {
|
||||||
|
IAIIconButtonProps,
|
||||||
|
} from 'common/components/IAIIconButton';
|
||||||
|
import ProgressBar from 'features/system/components/ProgressBar';
|
||||||
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FaPlay } from 'react-icons/fa';
|
||||||
|
|
||||||
|
interface InvokeButton
|
||||||
|
extends Omit<IAIButtonProps | IAIIconButtonProps, 'aria-label'> {
|
||||||
|
iconButton?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NodeInvokeButton(props: InvokeButton) {
|
||||||
|
const { iconButton = false, ...rest } = props;
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { isReady } = useAppSelector(readinessSelector);
|
||||||
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
|
||||||
|
const handleInvoke = useCallback(() => {
|
||||||
|
dispatch(userInvoked('nodes'));
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useHotkeys(
|
||||||
|
['ctrl+enter', 'meta+enter'],
|
||||||
|
handleInvoke,
|
||||||
|
{
|
||||||
|
enabled: () => isReady,
|
||||||
|
preventDefault: true,
|
||||||
|
enableOnFormTags: ['input', 'textarea', 'select'],
|
||||||
|
},
|
||||||
|
[isReady, activeTabName]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box style={{ flexGrow: 4 }} position="relative">
|
||||||
|
<Box style={{ position: 'relative' }}>
|
||||||
|
{!isReady && (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '0',
|
||||||
|
left: '0',
|
||||||
|
right: '0',
|
||||||
|
height: '100%',
|
||||||
|
overflow: 'clip',
|
||||||
|
borderRadius: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProgressBar />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{iconButton ? (
|
||||||
|
<IAIIconButton
|
||||||
|
aria-label={t('parameters.invoke')}
|
||||||
|
type="submit"
|
||||||
|
icon={<FaPlay />}
|
||||||
|
isDisabled={!isReady}
|
||||||
|
onClick={handleInvoke}
|
||||||
|
flexGrow={1}
|
||||||
|
w="100%"
|
||||||
|
tooltip={t('parameters.invoke')}
|
||||||
|
tooltipProps={{ placement: 'bottom' }}
|
||||||
|
colorScheme="accent"
|
||||||
|
id="invoke-button"
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<IAIButton
|
||||||
|
aria-label={t('parameters.invoke')}
|
||||||
|
type="submit"
|
||||||
|
isDisabled={!isReady}
|
||||||
|
onClick={handleInvoke}
|
||||||
|
flexGrow={1}
|
||||||
|
w="100%"
|
||||||
|
colorScheme="accent"
|
||||||
|
id="invoke-button"
|
||||||
|
fontWeight={700}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
Invoke
|
||||||
|
</IAIButton>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@ -16,7 +16,7 @@ import { buildEdges } from '../edgeBuilders/buildEdges';
|
|||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
import { buildInpaintNode } from '../nodeBuilders/buildInpaintNode';
|
import { buildInpaintNode } from '../nodeBuilders/buildInpaintNode';
|
||||||
|
|
||||||
const moduleLog = log.child({ namespace: 'buildCanvasGraph' });
|
const moduleLog = log.child({ namespace: 'nodes' });
|
||||||
|
|
||||||
const buildBaseNode = (
|
const buildBaseNode = (
|
||||||
nodeType: 'txt2img' | 'img2img' | 'inpaint' | 'outpaint',
|
nodeType: 'txt2img' | 'img2img' | 'inpaint' | 'outpaint',
|
||||||
@ -80,18 +80,23 @@ export const buildCanvasGraphComponents = async (
|
|||||||
infillMethod,
|
infillMethod,
|
||||||
} = state.generation;
|
} = state.generation;
|
||||||
|
|
||||||
// generationParameters.invert_mask = shouldPreserveMaskedArea;
|
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } =
|
||||||
// if (boundingBoxScale !== 'none') {
|
state.canvas;
|
||||||
// generationParameters.inpaint_width = scaledBoundingBoxDimensions.width;
|
|
||||||
// generationParameters.inpaint_height = scaledBoundingBoxDimensions.height;
|
if (boundingBoxScaleMethod !== 'none') {
|
||||||
// }
|
baseNode.inpaint_width = scaledBoundingBoxDimensions.width;
|
||||||
|
baseNode.inpaint_height = scaledBoundingBoxDimensions.height;
|
||||||
|
}
|
||||||
|
|
||||||
baseNode.seam_size = seamSize;
|
baseNode.seam_size = seamSize;
|
||||||
baseNode.seam_blur = seamBlur;
|
baseNode.seam_blur = seamBlur;
|
||||||
baseNode.seam_strength = seamStrength;
|
baseNode.seam_strength = seamStrength;
|
||||||
baseNode.seam_steps = seamSteps;
|
baseNode.seam_steps = seamSteps;
|
||||||
baseNode.tile_size = tileSize;
|
|
||||||
baseNode.infill_method = infillMethod as InpaintInvocation['infill_method'];
|
baseNode.infill_method = infillMethod as InpaintInvocation['infill_method'];
|
||||||
// baseNode.force_outpaint = false;
|
|
||||||
|
if (infillMethod === 'tile') {
|
||||||
|
baseNode.tile_size = tileSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We always range and iterate nodes, no matter the iteration count
|
// We always range and iterate nodes, no matter the iteration count
|
||||||
|
@ -2,21 +2,31 @@ import { RootState } from 'app/store/store';
|
|||||||
import {
|
import {
|
||||||
CompelInvocation,
|
CompelInvocation,
|
||||||
Graph,
|
Graph,
|
||||||
|
ImageResizeInvocation,
|
||||||
ImageToLatentsInvocation,
|
ImageToLatentsInvocation,
|
||||||
|
IterateInvocation,
|
||||||
LatentsToImageInvocation,
|
LatentsToImageInvocation,
|
||||||
LatentsToLatentsInvocation,
|
LatentsToLatentsInvocation,
|
||||||
|
NoiseInvocation,
|
||||||
|
RandomIntInvocation,
|
||||||
|
RangeOfSizeInvocation,
|
||||||
} from 'services/api';
|
} from 'services/api';
|
||||||
import { NonNullableGraph } from 'features/nodes/types/types';
|
import { NonNullableGraph } from 'features/nodes/types/types';
|
||||||
import { addNoiseNodes } from '../nodeBuilders/addNoiseNodes';
|
|
||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
|
import { set } from 'lodash-es';
|
||||||
|
|
||||||
const moduleLog = log.child({ namespace: 'buildImageToImageGraph' });
|
const moduleLog = log.child({ namespace: 'nodes' });
|
||||||
|
|
||||||
const POSITIVE_CONDITIONING = 'positive_conditioning';
|
const POSITIVE_CONDITIONING = 'positive_conditioning';
|
||||||
const NEGATIVE_CONDITIONING = 'negative_conditioning';
|
const NEGATIVE_CONDITIONING = 'negative_conditioning';
|
||||||
const IMAGE_TO_LATENTS = 'image_to_latents';
|
const IMAGE_TO_LATENTS = 'image_to_latents';
|
||||||
const LATENTS_TO_LATENTS = 'latents_to_latents';
|
const LATENTS_TO_LATENTS = 'latents_to_latents';
|
||||||
const LATENTS_TO_IMAGE = 'latents_to_image';
|
const LATENTS_TO_IMAGE = 'latents_to_image';
|
||||||
|
const RESIZE = 'resize_image';
|
||||||
|
const NOISE = 'noise';
|
||||||
|
const RANDOM_INT = 'rand_int';
|
||||||
|
const RANGE_OF_SIZE = 'range_of_size';
|
||||||
|
const ITERATE = 'iterate';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the Image to Image tab graph.
|
* Builds the Image to Image tab graph.
|
||||||
@ -31,6 +41,12 @@ export const buildImageToImageGraph = (state: RootState): Graph => {
|
|||||||
steps,
|
steps,
|
||||||
initialImage,
|
initialImage,
|
||||||
img2imgStrength: strength,
|
img2imgStrength: strength,
|
||||||
|
shouldFitToWidthHeight,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
iterations,
|
||||||
|
seed,
|
||||||
|
shouldRandomizeSeed,
|
||||||
} = state.generation;
|
} = state.generation;
|
||||||
|
|
||||||
if (!initialImage) {
|
if (!initialImage) {
|
||||||
@ -38,12 +54,12 @@ export const buildImageToImageGraph = (state: RootState): Graph => {
|
|||||||
throw new Error('No initial image found in state');
|
throw new Error('No initial image found in state');
|
||||||
}
|
}
|
||||||
|
|
||||||
let graph: NonNullableGraph = {
|
const graph: NonNullableGraph = {
|
||||||
nodes: {},
|
nodes: {},
|
||||||
edges: [],
|
edges: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the conditioning, t2l and l2i nodes
|
// Create the positive conditioning (prompt) node
|
||||||
const positiveConditioningNode: CompelInvocation = {
|
const positiveConditioningNode: CompelInvocation = {
|
||||||
id: POSITIVE_CONDITIONING,
|
id: POSITIVE_CONDITIONING,
|
||||||
type: 'compel',
|
type: 'compel',
|
||||||
@ -51,6 +67,7 @@ export const buildImageToImageGraph = (state: RootState): Graph => {
|
|||||||
model,
|
model,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Negative conditioning
|
||||||
const negativeConditioningNode: CompelInvocation = {
|
const negativeConditioningNode: CompelInvocation = {
|
||||||
id: NEGATIVE_CONDITIONING,
|
id: NEGATIVE_CONDITIONING,
|
||||||
type: 'compel',
|
type: 'compel',
|
||||||
@ -58,16 +75,15 @@ export const buildImageToImageGraph = (state: RootState): Graph => {
|
|||||||
model,
|
model,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This will encode the raster image to latents - but it may get its `image` from a resize node,
|
||||||
|
// so we do not set its `image` property yet
|
||||||
const imageToLatentsNode: ImageToLatentsInvocation = {
|
const imageToLatentsNode: ImageToLatentsInvocation = {
|
||||||
id: IMAGE_TO_LATENTS,
|
id: IMAGE_TO_LATENTS,
|
||||||
type: 'i2l',
|
type: 'i2l',
|
||||||
model,
|
model,
|
||||||
image: {
|
|
||||||
image_name: initialImage?.image_name,
|
|
||||||
image_origin: initialImage?.image_origin,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This does the actual img2img inference
|
||||||
const latentsToLatentsNode: LatentsToLatentsInvocation = {
|
const latentsToLatentsNode: LatentsToLatentsInvocation = {
|
||||||
id: LATENTS_TO_LATENTS,
|
id: LATENTS_TO_LATENTS,
|
||||||
type: 'l2l',
|
type: 'l2l',
|
||||||
@ -78,20 +94,21 @@ export const buildImageToImageGraph = (state: RootState): Graph => {
|
|||||||
strength,
|
strength,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Finally we decode the latents back to an image
|
||||||
const latentsToImageNode: LatentsToImageInvocation = {
|
const latentsToImageNode: LatentsToImageInvocation = {
|
||||||
id: LATENTS_TO_IMAGE,
|
id: LATENTS_TO_IMAGE,
|
||||||
type: 'l2i',
|
type: 'l2i',
|
||||||
model,
|
model,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add to the graph
|
// Add all those nodes to the graph
|
||||||
graph.nodes[POSITIVE_CONDITIONING] = positiveConditioningNode;
|
graph.nodes[POSITIVE_CONDITIONING] = positiveConditioningNode;
|
||||||
graph.nodes[NEGATIVE_CONDITIONING] = negativeConditioningNode;
|
graph.nodes[NEGATIVE_CONDITIONING] = negativeConditioningNode;
|
||||||
graph.nodes[IMAGE_TO_LATENTS] = imageToLatentsNode;
|
graph.nodes[IMAGE_TO_LATENTS] = imageToLatentsNode;
|
||||||
graph.nodes[LATENTS_TO_LATENTS] = latentsToLatentsNode;
|
graph.nodes[LATENTS_TO_LATENTS] = latentsToLatentsNode;
|
||||||
graph.nodes[LATENTS_TO_IMAGE] = latentsToImageNode;
|
graph.nodes[LATENTS_TO_IMAGE] = latentsToImageNode;
|
||||||
|
|
||||||
// Connect them
|
// Connect the prompt nodes to the imageToLatents node
|
||||||
graph.edges.push({
|
graph.edges.push({
|
||||||
source: { node_id: POSITIVE_CONDITIONING, field: 'conditioning' },
|
source: { node_id: POSITIVE_CONDITIONING, field: 'conditioning' },
|
||||||
destination: {
|
destination: {
|
||||||
@ -99,7 +116,6 @@ export const buildImageToImageGraph = (state: RootState): Graph => {
|
|||||||
field: 'positive_conditioning',
|
field: 'positive_conditioning',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
graph.edges.push({
|
graph.edges.push({
|
||||||
source: { node_id: NEGATIVE_CONDITIONING, field: 'conditioning' },
|
source: { node_id: NEGATIVE_CONDITIONING, field: 'conditioning' },
|
||||||
destination: {
|
destination: {
|
||||||
@ -108,6 +124,7 @@ export const buildImageToImageGraph = (state: RootState): Graph => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Connect the image-encoding node
|
||||||
graph.edges.push({
|
graph.edges.push({
|
||||||
source: { node_id: IMAGE_TO_LATENTS, field: 'latents' },
|
source: { node_id: IMAGE_TO_LATENTS, field: 'latents' },
|
||||||
destination: {
|
destination: {
|
||||||
@ -116,6 +133,7 @@ export const buildImageToImageGraph = (state: RootState): Graph => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Connect the image-decoding node
|
||||||
graph.edges.push({
|
graph.edges.push({
|
||||||
source: { node_id: LATENTS_TO_LATENTS, field: 'latents' },
|
source: { node_id: LATENTS_TO_LATENTS, field: 'latents' },
|
||||||
destination: {
|
destination: {
|
||||||
@ -124,8 +142,271 @@ export const buildImageToImageGraph = (state: RootState): Graph => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create and add the noise nodes
|
/**
|
||||||
graph = addNoiseNodes(graph, latentsToLatentsNode.id, state);
|
* Now we need to handle iterations and random seeds. There are four possible scenarios:
|
||||||
|
* - Single iteration, explicit seed
|
||||||
|
* - Single iteration, random seed
|
||||||
|
* - Multiple iterations, explicit seed
|
||||||
|
* - Multiple iterations, random seed
|
||||||
|
*
|
||||||
|
* They all have different graphs and connections.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Single iteration, explicit seed
|
||||||
|
if (!shouldRandomizeSeed && iterations === 1) {
|
||||||
|
// Noise node using the explicit seed
|
||||||
|
const noiseNode: NoiseInvocation = {
|
||||||
|
id: NOISE,
|
||||||
|
type: 'noise',
|
||||||
|
seed: seed,
|
||||||
|
};
|
||||||
|
|
||||||
|
graph.nodes[NOISE] = noiseNode;
|
||||||
|
|
||||||
|
// Connect noise to l2l
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: NOISE, field: 'noise' },
|
||||||
|
destination: {
|
||||||
|
node_id: LATENTS_TO_LATENTS,
|
||||||
|
field: 'noise',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single iteration, random seed
|
||||||
|
if (shouldRandomizeSeed && iterations === 1) {
|
||||||
|
// Random int node to generate the seed
|
||||||
|
const randomIntNode: RandomIntInvocation = {
|
||||||
|
id: RANDOM_INT,
|
||||||
|
type: 'rand_int',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Noise node without any seed
|
||||||
|
const noiseNode: NoiseInvocation = {
|
||||||
|
id: NOISE,
|
||||||
|
type: 'noise',
|
||||||
|
};
|
||||||
|
|
||||||
|
graph.nodes[RANDOM_INT] = randomIntNode;
|
||||||
|
graph.nodes[NOISE] = noiseNode;
|
||||||
|
|
||||||
|
// Connect random int to the seed of the noise node
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RANDOM_INT, field: 'a' },
|
||||||
|
destination: {
|
||||||
|
node_id: NOISE,
|
||||||
|
field: 'seed',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect noise to l2l
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: NOISE, field: 'noise' },
|
||||||
|
destination: {
|
||||||
|
node_id: LATENTS_TO_LATENTS,
|
||||||
|
field: 'noise',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple iterations, explicit seed
|
||||||
|
if (!shouldRandomizeSeed && iterations > 1) {
|
||||||
|
// Range of size node to generate `iterations` count of seeds - range of size generates a collection
|
||||||
|
// of ints from `start` to `start + size`. The `start` is the seed, and the `size` is the number of
|
||||||
|
// iterations.
|
||||||
|
const rangeOfSizeNode: RangeOfSizeInvocation = {
|
||||||
|
id: RANGE_OF_SIZE,
|
||||||
|
type: 'range_of_size',
|
||||||
|
start: seed,
|
||||||
|
size: iterations,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate node to iterate over the seeds generated by the range of size node
|
||||||
|
const iterateNode: IterateInvocation = {
|
||||||
|
id: ITERATE,
|
||||||
|
type: 'iterate',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Noise node without any seed
|
||||||
|
const noiseNode: NoiseInvocation = {
|
||||||
|
id: NOISE,
|
||||||
|
type: 'noise',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adding to the graph
|
||||||
|
graph.nodes[RANGE_OF_SIZE] = rangeOfSizeNode;
|
||||||
|
graph.nodes[ITERATE] = iterateNode;
|
||||||
|
graph.nodes[NOISE] = noiseNode;
|
||||||
|
|
||||||
|
// Connect range of size to iterate
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RANGE_OF_SIZE, field: 'collection' },
|
||||||
|
destination: {
|
||||||
|
node_id: ITERATE,
|
||||||
|
field: 'collection',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect iterate to noise
|
||||||
|
graph.edges.push({
|
||||||
|
source: {
|
||||||
|
node_id: ITERATE,
|
||||||
|
field: 'item',
|
||||||
|
},
|
||||||
|
destination: {
|
||||||
|
node_id: NOISE,
|
||||||
|
field: 'seed',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect noise to l2l
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: NOISE, field: 'noise' },
|
||||||
|
destination: {
|
||||||
|
node_id: LATENTS_TO_LATENTS,
|
||||||
|
field: 'noise',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple iterations, random seed
|
||||||
|
if (shouldRandomizeSeed && iterations > 1) {
|
||||||
|
// Random int node to generate the seed
|
||||||
|
const randomIntNode: RandomIntInvocation = {
|
||||||
|
id: RANDOM_INT,
|
||||||
|
type: 'rand_int',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Range of size node to generate `iterations` count of seeds - range of size generates a collection
|
||||||
|
const rangeOfSizeNode: RangeOfSizeInvocation = {
|
||||||
|
id: RANGE_OF_SIZE,
|
||||||
|
type: 'range_of_size',
|
||||||
|
size: iterations,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate node to iterate over the seeds generated by the range of size node
|
||||||
|
const iterateNode: IterateInvocation = {
|
||||||
|
id: ITERATE,
|
||||||
|
type: 'iterate',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Noise node without any seed
|
||||||
|
const noiseNode: NoiseInvocation = {
|
||||||
|
id: NOISE,
|
||||||
|
type: 'noise',
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adding to the graph
|
||||||
|
graph.nodes[RANDOM_INT] = randomIntNode;
|
||||||
|
graph.nodes[RANGE_OF_SIZE] = rangeOfSizeNode;
|
||||||
|
graph.nodes[ITERATE] = iterateNode;
|
||||||
|
graph.nodes[NOISE] = noiseNode;
|
||||||
|
|
||||||
|
// Connect random int to the start of the range of size so the range starts on the random first seed
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RANDOM_INT, field: 'a' },
|
||||||
|
destination: { node_id: RANGE_OF_SIZE, field: 'start' },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect range of size to iterate
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RANGE_OF_SIZE, field: 'collection' },
|
||||||
|
destination: {
|
||||||
|
node_id: ITERATE,
|
||||||
|
field: 'collection',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect iterate to noise
|
||||||
|
graph.edges.push({
|
||||||
|
source: {
|
||||||
|
node_id: ITERATE,
|
||||||
|
field: 'item',
|
||||||
|
},
|
||||||
|
destination: {
|
||||||
|
node_id: NOISE,
|
||||||
|
field: 'seed',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect noise to l2l
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: NOISE, field: 'noise' },
|
||||||
|
destination: {
|
||||||
|
node_id: LATENTS_TO_LATENTS,
|
||||||
|
field: 'noise',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFitToWidthHeight) {
|
||||||
|
// The init image needs to be resized to the specified width and height before being passed to `IMAGE_TO_LATENTS`
|
||||||
|
|
||||||
|
// Create a resize node, explicitly setting its image
|
||||||
|
const resizeNode: ImageResizeInvocation = {
|
||||||
|
id: RESIZE,
|
||||||
|
type: 'img_resize',
|
||||||
|
image: {
|
||||||
|
image_name: initialImage.image_name,
|
||||||
|
image_origin: initialImage.image_origin,
|
||||||
|
},
|
||||||
|
is_intermediate: true,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
};
|
||||||
|
|
||||||
|
graph.nodes[RESIZE] = resizeNode;
|
||||||
|
|
||||||
|
// The `RESIZE` node then passes its image to `IMAGE_TO_LATENTS`
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RESIZE, field: 'image' },
|
||||||
|
destination: {
|
||||||
|
node_id: IMAGE_TO_LATENTS,
|
||||||
|
field: 'image',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// The `RESIZE` node also passes its width and height to `NOISE`
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RESIZE, field: 'width' },
|
||||||
|
destination: {
|
||||||
|
node_id: NOISE,
|
||||||
|
field: 'width',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RESIZE, field: 'height' },
|
||||||
|
destination: {
|
||||||
|
node_id: NOISE,
|
||||||
|
field: 'height',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// We are not resizing, so we need to set the image on the `IMAGE_TO_LATENTS` node explicitly
|
||||||
|
set(graph.nodes[IMAGE_TO_LATENTS], 'image', {
|
||||||
|
image_name: initialImage.image_name,
|
||||||
|
image_origin: initialImage.image_origin,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pass the image's dimensions to the `NOISE` node
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: IMAGE_TO_LATENTS, field: 'width' },
|
||||||
|
destination: {
|
||||||
|
node_id: NOISE,
|
||||||
|
field: 'width',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: IMAGE_TO_LATENTS, field: 'height' },
|
||||||
|
destination: {
|
||||||
|
node_id: NOISE,
|
||||||
|
field: 'height',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return graph;
|
return graph;
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { Graph } from 'services/api';
|
import { Graph } from 'services/api';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { cloneDeep, reduce } from 'lodash-es';
|
import { cloneDeep, forEach, omit, reduce, values } from 'lodash-es';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { InputFieldValue } from 'features/nodes/types/types';
|
import { InputFieldValue } from 'features/nodes/types/types';
|
||||||
|
import { AnyInvocation } from 'services/events/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We need to do special handling for some fields
|
* We need to do special handling for some fields
|
||||||
@ -89,6 +90,24 @@ export const buildNodesGraph = (state: RootState): Graph => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Omit all inputs that have edges connected.
|
||||||
|
*
|
||||||
|
* Fixes edge case where the user has connected an input, but also provided an invalid explicit,
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* In this edge case, pydantic will invalidate the node based on the invalid explicit value,
|
||||||
|
* even though the actual value that will be used comes from the connection.
|
||||||
|
*/
|
||||||
|
parsedEdges.forEach((edge) => {
|
||||||
|
const destination_node = parsedNodes[edge.destination.node_id];
|
||||||
|
const field = edge.destination.field;
|
||||||
|
parsedNodes[edge.destination.node_id] = omit(
|
||||||
|
destination_node,
|
||||||
|
field
|
||||||
|
) as AnyInvocation;
|
||||||
|
});
|
||||||
|
|
||||||
// Assemble!
|
// Assemble!
|
||||||
const graph = {
|
const graph = {
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
|
@ -2,16 +2,23 @@ import { RootState } from 'app/store/store';
|
|||||||
import {
|
import {
|
||||||
CompelInvocation,
|
CompelInvocation,
|
||||||
Graph,
|
Graph,
|
||||||
|
IterateInvocation,
|
||||||
LatentsToImageInvocation,
|
LatentsToImageInvocation,
|
||||||
|
NoiseInvocation,
|
||||||
|
RandomIntInvocation,
|
||||||
|
RangeOfSizeInvocation,
|
||||||
TextToLatentsInvocation,
|
TextToLatentsInvocation,
|
||||||
} from 'services/api';
|
} from 'services/api';
|
||||||
import { NonNullableGraph } from 'features/nodes/types/types';
|
import { NonNullableGraph } from 'features/nodes/types/types';
|
||||||
import { addNoiseNodes } from '../nodeBuilders/addNoiseNodes';
|
|
||||||
|
|
||||||
const POSITIVE_CONDITIONING = 'positive_conditioning';
|
const POSITIVE_CONDITIONING = 'positive_conditioning';
|
||||||
const NEGATIVE_CONDITIONING = 'negative_conditioning';
|
const NEGATIVE_CONDITIONING = 'negative_conditioning';
|
||||||
const TEXT_TO_LATENTS = 'text_to_latents';
|
const TEXT_TO_LATENTS = 'text_to_latents';
|
||||||
const LATENTS_TO_IMAGE = 'latents_to_image';
|
const LATENTS_TO_IMAGE = 'latents_to_image';
|
||||||
|
const NOISE = 'noise';
|
||||||
|
const RANDOM_INT = 'rand_int';
|
||||||
|
const RANGE_OF_SIZE = 'range_of_size';
|
||||||
|
const ITERATE = 'iterate';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the Text to Image tab graph.
|
* Builds the Text to Image tab graph.
|
||||||
@ -24,9 +31,14 @@ export const buildTextToImageGraph = (state: RootState): Graph => {
|
|||||||
cfgScale: cfg_scale,
|
cfgScale: cfg_scale,
|
||||||
scheduler,
|
scheduler,
|
||||||
steps,
|
steps,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
iterations,
|
||||||
|
seed,
|
||||||
|
shouldRandomizeSeed,
|
||||||
} = state.generation;
|
} = state.generation;
|
||||||
|
|
||||||
let graph: NonNullableGraph = {
|
const graph: NonNullableGraph = {
|
||||||
nodes: {},
|
nodes: {},
|
||||||
edges: [],
|
edges: [],
|
||||||
};
|
};
|
||||||
@ -92,8 +104,209 @@ export const buildTextToImageGraph = (state: RootState): Graph => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create and add the noise nodes
|
/**
|
||||||
graph = addNoiseNodes(graph, TEXT_TO_LATENTS, state);
|
* Now we need to handle iterations and random seeds. There are four possible scenarios:
|
||||||
|
* - Single iteration, explicit seed
|
||||||
|
* - Single iteration, random seed
|
||||||
|
* - Multiple iterations, explicit seed
|
||||||
|
* - Multiple iterations, random seed
|
||||||
|
*
|
||||||
|
* They all have different graphs and connections.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Single iteration, explicit seed
|
||||||
|
if (!shouldRandomizeSeed && iterations === 1) {
|
||||||
|
// Noise node using the explicit seed
|
||||||
|
const noiseNode: NoiseInvocation = {
|
||||||
|
id: NOISE,
|
||||||
|
type: 'noise',
|
||||||
|
seed: seed,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
|
||||||
|
graph.nodes[NOISE] = noiseNode;
|
||||||
|
|
||||||
|
// Connect noise to l2l
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: NOISE, field: 'noise' },
|
||||||
|
destination: {
|
||||||
|
node_id: TEXT_TO_LATENTS,
|
||||||
|
field: 'noise',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single iteration, random seed
|
||||||
|
if (shouldRandomizeSeed && iterations === 1) {
|
||||||
|
// Random int node to generate the seed
|
||||||
|
const randomIntNode: RandomIntInvocation = {
|
||||||
|
id: RANDOM_INT,
|
||||||
|
type: 'rand_int',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Noise node without any seed
|
||||||
|
const noiseNode: NoiseInvocation = {
|
||||||
|
id: NOISE,
|
||||||
|
type: 'noise',
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
|
||||||
|
graph.nodes[RANDOM_INT] = randomIntNode;
|
||||||
|
graph.nodes[NOISE] = noiseNode;
|
||||||
|
|
||||||
|
// Connect random int to the seed of the noise node
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RANDOM_INT, field: 'a' },
|
||||||
|
destination: {
|
||||||
|
node_id: NOISE,
|
||||||
|
field: 'seed',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect noise to t2l
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: NOISE, field: 'noise' },
|
||||||
|
destination: {
|
||||||
|
node_id: TEXT_TO_LATENTS,
|
||||||
|
field: 'noise',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple iterations, explicit seed
|
||||||
|
if (!shouldRandomizeSeed && iterations > 1) {
|
||||||
|
// Range of size node to generate `iterations` count of seeds - range of size generates a collection
|
||||||
|
// of ints from `start` to `start + size`. The `start` is the seed, and the `size` is the number of
|
||||||
|
// iterations.
|
||||||
|
const rangeOfSizeNode: RangeOfSizeInvocation = {
|
||||||
|
id: RANGE_OF_SIZE,
|
||||||
|
type: 'range_of_size',
|
||||||
|
start: seed,
|
||||||
|
size: iterations,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate node to iterate over the seeds generated by the range of size node
|
||||||
|
const iterateNode: IterateInvocation = {
|
||||||
|
id: ITERATE,
|
||||||
|
type: 'iterate',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Noise node without any seed
|
||||||
|
const noiseNode: NoiseInvocation = {
|
||||||
|
id: NOISE,
|
||||||
|
type: 'noise',
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adding to the graph
|
||||||
|
graph.nodes[RANGE_OF_SIZE] = rangeOfSizeNode;
|
||||||
|
graph.nodes[ITERATE] = iterateNode;
|
||||||
|
graph.nodes[NOISE] = noiseNode;
|
||||||
|
|
||||||
|
// Connect range of size to iterate
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RANGE_OF_SIZE, field: 'collection' },
|
||||||
|
destination: {
|
||||||
|
node_id: ITERATE,
|
||||||
|
field: 'collection',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect iterate to noise
|
||||||
|
graph.edges.push({
|
||||||
|
source: {
|
||||||
|
node_id: ITERATE,
|
||||||
|
field: 'item',
|
||||||
|
},
|
||||||
|
destination: {
|
||||||
|
node_id: NOISE,
|
||||||
|
field: 'seed',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect noise to t2l
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: NOISE, field: 'noise' },
|
||||||
|
destination: {
|
||||||
|
node_id: TEXT_TO_LATENTS,
|
||||||
|
field: 'noise',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple iterations, random seed
|
||||||
|
if (shouldRandomizeSeed && iterations > 1) {
|
||||||
|
// Random int node to generate the seed
|
||||||
|
const randomIntNode: RandomIntInvocation = {
|
||||||
|
id: RANDOM_INT,
|
||||||
|
type: 'rand_int',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Range of size node to generate `iterations` count of seeds - range of size generates a collection
|
||||||
|
const rangeOfSizeNode: RangeOfSizeInvocation = {
|
||||||
|
id: RANGE_OF_SIZE,
|
||||||
|
type: 'range_of_size',
|
||||||
|
size: iterations,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate node to iterate over the seeds generated by the range of size node
|
||||||
|
const iterateNode: IterateInvocation = {
|
||||||
|
id: ITERATE,
|
||||||
|
type: 'iterate',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Noise node without any seed
|
||||||
|
const noiseNode: NoiseInvocation = {
|
||||||
|
id: NOISE,
|
||||||
|
type: 'noise',
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adding to the graph
|
||||||
|
graph.nodes[RANDOM_INT] = randomIntNode;
|
||||||
|
graph.nodes[RANGE_OF_SIZE] = rangeOfSizeNode;
|
||||||
|
graph.nodes[ITERATE] = iterateNode;
|
||||||
|
graph.nodes[NOISE] = noiseNode;
|
||||||
|
|
||||||
|
// Connect random int to the start of the range of size so the range starts on the random first seed
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RANDOM_INT, field: 'a' },
|
||||||
|
destination: { node_id: RANGE_OF_SIZE, field: 'start' },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect range of size to iterate
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: RANGE_OF_SIZE, field: 'collection' },
|
||||||
|
destination: {
|
||||||
|
node_id: ITERATE,
|
||||||
|
field: 'collection',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect iterate to noise
|
||||||
|
graph.edges.push({
|
||||||
|
source: {
|
||||||
|
node_id: ITERATE,
|
||||||
|
field: 'item',
|
||||||
|
},
|
||||||
|
destination: {
|
||||||
|
node_id: NOISE,
|
||||||
|
field: 'seed',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect noise to t2l
|
||||||
|
graph.edges.push({
|
||||||
|
source: { node_id: NOISE, field: 'noise' },
|
||||||
|
destination: {
|
||||||
|
node_id: TEXT_TO_LATENTS,
|
||||||
|
field: 'noise',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
return graph;
|
return graph;
|
||||||
};
|
};
|
||||||
|
@ -1,208 +0,0 @@
|
|||||||
import { RootState } from 'app/store/store';
|
|
||||||
import {
|
|
||||||
IterateInvocation,
|
|
||||||
NoiseInvocation,
|
|
||||||
RandomIntInvocation,
|
|
||||||
RangeOfSizeInvocation,
|
|
||||||
} from 'services/api';
|
|
||||||
import { NonNullableGraph } from 'features/nodes/types/types';
|
|
||||||
import { cloneDeep } from 'lodash-es';
|
|
||||||
|
|
||||||
const NOISE = 'noise';
|
|
||||||
const RANDOM_INT = 'rand_int';
|
|
||||||
const RANGE_OF_SIZE = 'range_of_size';
|
|
||||||
const ITERATE = 'iterate';
|
|
||||||
/**
|
|
||||||
* Adds the appropriate noise nodes to a linear UI t2l or l2l graph.
|
|
||||||
*
|
|
||||||
* @param graph The graph to add the noise nodes to.
|
|
||||||
* @param baseNodeId The id of the base node to connect the noise nodes to.
|
|
||||||
* @param state The app state..
|
|
||||||
*/
|
|
||||||
export const addNoiseNodes = (
|
|
||||||
graph: NonNullableGraph,
|
|
||||||
baseNodeId: string,
|
|
||||||
state: RootState
|
|
||||||
): NonNullableGraph => {
|
|
||||||
const graphClone = cloneDeep(graph);
|
|
||||||
|
|
||||||
// Create and add the noise nodes
|
|
||||||
const { width, height, seed, iterations, shouldRandomizeSeed } =
|
|
||||||
state.generation;
|
|
||||||
|
|
||||||
// Single iteration, explicit seed
|
|
||||||
if (!shouldRandomizeSeed && iterations === 1) {
|
|
||||||
const noiseNode: NoiseInvocation = {
|
|
||||||
id: NOISE,
|
|
||||||
type: 'noise',
|
|
||||||
seed: seed,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
};
|
|
||||||
|
|
||||||
graphClone.nodes[NOISE] = noiseNode;
|
|
||||||
|
|
||||||
// Connect them
|
|
||||||
graphClone.edges.push({
|
|
||||||
source: { node_id: NOISE, field: 'noise' },
|
|
||||||
destination: {
|
|
||||||
node_id: baseNodeId,
|
|
||||||
field: 'noise',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single iteration, random seed
|
|
||||||
if (shouldRandomizeSeed && iterations === 1) {
|
|
||||||
// TODO: This assumes the `high` value is the max seed value
|
|
||||||
const randomIntNode: RandomIntInvocation = {
|
|
||||||
id: RANDOM_INT,
|
|
||||||
type: 'rand_int',
|
|
||||||
};
|
|
||||||
|
|
||||||
const noiseNode: NoiseInvocation = {
|
|
||||||
id: NOISE,
|
|
||||||
type: 'noise',
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
};
|
|
||||||
|
|
||||||
graphClone.nodes[RANDOM_INT] = randomIntNode;
|
|
||||||
graphClone.nodes[NOISE] = noiseNode;
|
|
||||||
|
|
||||||
graphClone.edges.push({
|
|
||||||
source: { node_id: RANDOM_INT, field: 'a' },
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'seed',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
graphClone.edges.push({
|
|
||||||
source: { node_id: NOISE, field: 'noise' },
|
|
||||||
destination: {
|
|
||||||
node_id: baseNodeId,
|
|
||||||
field: 'noise',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiple iterations, explicit seed
|
|
||||||
if (!shouldRandomizeSeed && iterations > 1) {
|
|
||||||
const rangeOfSizeNode: RangeOfSizeInvocation = {
|
|
||||||
id: RANGE_OF_SIZE,
|
|
||||||
type: 'range_of_size',
|
|
||||||
start: seed,
|
|
||||||
size: iterations,
|
|
||||||
};
|
|
||||||
|
|
||||||
const iterateNode: IterateInvocation = {
|
|
||||||
id: ITERATE,
|
|
||||||
type: 'iterate',
|
|
||||||
};
|
|
||||||
|
|
||||||
const noiseNode: NoiseInvocation = {
|
|
||||||
id: NOISE,
|
|
||||||
type: 'noise',
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
};
|
|
||||||
|
|
||||||
graphClone.nodes[RANGE_OF_SIZE] = rangeOfSizeNode;
|
|
||||||
graphClone.nodes[ITERATE] = iterateNode;
|
|
||||||
graphClone.nodes[NOISE] = noiseNode;
|
|
||||||
|
|
||||||
graphClone.edges.push({
|
|
||||||
source: { node_id: RANGE_OF_SIZE, field: 'collection' },
|
|
||||||
destination: {
|
|
||||||
node_id: ITERATE,
|
|
||||||
field: 'collection',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
graphClone.edges.push({
|
|
||||||
source: {
|
|
||||||
node_id: ITERATE,
|
|
||||||
field: 'item',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'seed',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
graphClone.edges.push({
|
|
||||||
source: { node_id: NOISE, field: 'noise' },
|
|
||||||
destination: {
|
|
||||||
node_id: baseNodeId,
|
|
||||||
field: 'noise',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiple iterations, random seed
|
|
||||||
if (shouldRandomizeSeed && iterations > 1) {
|
|
||||||
// TODO: This assumes the `high` value is the max seed value
|
|
||||||
const randomIntNode: RandomIntInvocation = {
|
|
||||||
id: RANDOM_INT,
|
|
||||||
type: 'rand_int',
|
|
||||||
};
|
|
||||||
|
|
||||||
const rangeOfSizeNode: RangeOfSizeInvocation = {
|
|
||||||
id: RANGE_OF_SIZE,
|
|
||||||
type: 'range_of_size',
|
|
||||||
size: iterations,
|
|
||||||
};
|
|
||||||
|
|
||||||
const iterateNode: IterateInvocation = {
|
|
||||||
id: ITERATE,
|
|
||||||
type: 'iterate',
|
|
||||||
};
|
|
||||||
|
|
||||||
const noiseNode: NoiseInvocation = {
|
|
||||||
id: NOISE,
|
|
||||||
type: 'noise',
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
};
|
|
||||||
|
|
||||||
graphClone.nodes[RANDOM_INT] = randomIntNode;
|
|
||||||
graphClone.nodes[RANGE_OF_SIZE] = rangeOfSizeNode;
|
|
||||||
graphClone.nodes[ITERATE] = iterateNode;
|
|
||||||
graphClone.nodes[NOISE] = noiseNode;
|
|
||||||
|
|
||||||
graphClone.edges.push({
|
|
||||||
source: { node_id: RANDOM_INT, field: 'a' },
|
|
||||||
destination: { node_id: RANGE_OF_SIZE, field: 'start' },
|
|
||||||
});
|
|
||||||
|
|
||||||
graphClone.edges.push({
|
|
||||||
source: { node_id: RANGE_OF_SIZE, field: 'collection' },
|
|
||||||
destination: {
|
|
||||||
node_id: ITERATE,
|
|
||||||
field: 'collection',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
graphClone.edges.push({
|
|
||||||
source: {
|
|
||||||
node_id: ITERATE,
|
|
||||||
field: 'item',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'seed',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
graphClone.edges.push({
|
|
||||||
source: { node_id: NOISE, field: 'noise' },
|
|
||||||
destination: {
|
|
||||||
node_id: baseNodeId,
|
|
||||||
field: 'noise',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return graphClone;
|
|
||||||
};
|
|
@ -2,15 +2,12 @@ import { v4 as uuidv4 } from 'uuid';
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { InpaintInvocation } from 'services/api';
|
import { InpaintInvocation } from 'services/api';
|
||||||
import { O } from 'ts-toolbelt';
|
import { O } from 'ts-toolbelt';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
|
||||||
|
|
||||||
export const buildInpaintNode = (
|
export const buildInpaintNode = (
|
||||||
state: RootState,
|
state: RootState,
|
||||||
overrides: O.Partial<InpaintInvocation, 'deep'> = {}
|
overrides: O.Partial<InpaintInvocation, 'deep'> = {}
|
||||||
): InpaintInvocation => {
|
): InpaintInvocation => {
|
||||||
const nodeId = uuidv4();
|
const nodeId = uuidv4();
|
||||||
const { generation } = state;
|
|
||||||
const activeTabName = activeTabNameSelector(state);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
positivePrompt: prompt,
|
positivePrompt: prompt,
|
||||||
@ -25,8 +22,7 @@ export const buildInpaintNode = (
|
|||||||
img2imgStrength: strength,
|
img2imgStrength: strength,
|
||||||
shouldFitToWidthHeight: fit,
|
shouldFitToWidthHeight: fit,
|
||||||
shouldRandomizeSeed,
|
shouldRandomizeSeed,
|
||||||
initialImage,
|
} = state.generation;
|
||||||
} = generation;
|
|
||||||
|
|
||||||
const inpaintNode: InpaintInvocation = {
|
const inpaintNode: InpaintInvocation = {
|
||||||
id: nodeId,
|
id: nodeId,
|
||||||
@ -42,19 +38,6 @@ export const buildInpaintNode = (
|
|||||||
fit,
|
fit,
|
||||||
};
|
};
|
||||||
|
|
||||||
// on Canvas tab, we do not manually specific init image
|
|
||||||
if (activeTabName !== 'unifiedCanvas') {
|
|
||||||
if (!initialImage) {
|
|
||||||
// TODO: handle this more better
|
|
||||||
throw 'no initial image';
|
|
||||||
}
|
|
||||||
|
|
||||||
inpaintNode.image = {
|
|
||||||
image_name: initialImage.name,
|
|
||||||
image_origin: initialImage.type,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shouldRandomizeSeed) {
|
if (!shouldRandomizeSeed) {
|
||||||
inpaintNode.seed = seed;
|
inpaintNode.seed = seed;
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,22 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import {
|
||||||
|
canvasSelector,
|
||||||
|
isStagingSelector,
|
||||||
|
} from 'features/canvas/store/canvasSelectors';
|
||||||
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
canvasSelector,
|
[canvasSelector, isStagingSelector],
|
||||||
(canvas) => {
|
(canvas, isStaging) => {
|
||||||
const { boundingBoxDimensions } = canvas;
|
const { boundingBoxDimensions } = canvas;
|
||||||
return {
|
return {
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
|
isStaging,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
defaultSelectorOptions
|
defaultSelectorOptions
|
||||||
@ -21,7 +25,7 @@ const selector = createSelector(
|
|||||||
|
|
||||||
const ParamBoundingBoxWidth = () => {
|
const ParamBoundingBoxWidth = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { boundingBoxDimensions } = useAppSelector(selector);
|
const { boundingBoxDimensions, isStaging } = useAppSelector(selector);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@ -45,12 +49,13 @@ const ParamBoundingBoxWidth = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={t('parameters.height')}
|
label={t('parameters.boundingBoxHeight')}
|
||||||
min={64}
|
min={64}
|
||||||
max={1024}
|
max={1024}
|
||||||
step={64}
|
step={64}
|
||||||
value={boundingBoxDimensions.height}
|
value={boundingBoxDimensions.height}
|
||||||
onChange={handleChangeHeight}
|
onChange={handleChangeHeight}
|
||||||
|
isDisabled={isStaging}
|
||||||
sliderNumberInputProps={{ max: 4096 }}
|
sliderNumberInputProps={{ max: 4096 }}
|
||||||
withSliderMarks
|
withSliderMarks
|
||||||
withInput
|
withInput
|
||||||
|
@ -2,18 +2,22 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import {
|
||||||
|
canvasSelector,
|
||||||
|
isStagingSelector,
|
||||||
|
} from 'features/canvas/store/canvasSelectors';
|
||||||
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
canvasSelector,
|
[canvasSelector, isStagingSelector],
|
||||||
(canvas) => {
|
(canvas, isStaging) => {
|
||||||
const { boundingBoxDimensions } = canvas;
|
const { boundingBoxDimensions } = canvas;
|
||||||
return {
|
return {
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
|
isStaging,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
defaultSelectorOptions
|
defaultSelectorOptions
|
||||||
@ -21,7 +25,7 @@ const selector = createSelector(
|
|||||||
|
|
||||||
const ParamBoundingBoxWidth = () => {
|
const ParamBoundingBoxWidth = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { boundingBoxDimensions } = useAppSelector(selector);
|
const { boundingBoxDimensions, isStaging } = useAppSelector(selector);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@ -45,12 +49,13 @@ const ParamBoundingBoxWidth = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={t('parameters.width')}
|
label={t('parameters.boundingBoxWidth')}
|
||||||
min={64}
|
min={64}
|
||||||
max={1024}
|
max={1024}
|
||||||
step={64}
|
step={64}
|
||||||
value={boundingBoxDimensions.width}
|
value={boundingBoxDimensions.width}
|
||||||
onChange={handleChangeWidth}
|
onChange={handleChangeWidth}
|
||||||
|
isDisabled={isStaging}
|
||||||
sliderNumberInputProps={{ max: 4096 }}
|
sliderNumberInputProps={{ max: 4096 }}
|
||||||
withSliderMarks
|
withSliderMarks
|
||||||
withInput
|
withInput
|
||||||
|
@ -1,25 +1,34 @@
|
|||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { Scheduler } from 'app/constants';
|
import { Scheduler } from 'app/constants';
|
||||||
import { RootState } from 'app/store/store';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAICustomSelect from 'common/components/IAICustomSelect';
|
import IAICustomSelect from 'common/components/IAICustomSelect';
|
||||||
|
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||||
import { setScheduler } from 'features/parameters/store/generationSlice';
|
import { setScheduler } from 'features/parameters/store/generationSlice';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const ParamScheduler = () => {
|
const selector = createSelector(
|
||||||
const scheduler = useAppSelector(
|
[uiSelector, generationSelector],
|
||||||
(state: RootState) => state.generation.scheduler
|
(ui, generation) => {
|
||||||
);
|
// TODO: DPMSolverSinglestepScheduler is fixed in https://github.com/huggingface/diffusers/pull/3413
|
||||||
|
// but we need to wait for the next release before removing this special handling.
|
||||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
const allSchedulers = ui.schedulers.filter((scheduler) => {
|
||||||
|
|
||||||
const schedulers = useAppSelector((state: RootState) => state.ui.schedulers);
|
|
||||||
|
|
||||||
const img2imgSchedulers = schedulers.filter((scheduler) => {
|
|
||||||
return !['dpmpp_2s'].includes(scheduler);
|
return !['dpmpp_2s'].includes(scheduler);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
scheduler: generation.scheduler,
|
||||||
|
allSchedulers,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
defaultSelectorOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
const ParamScheduler = () => {
|
||||||
|
const { allSchedulers, scheduler } = useAppSelector(selector);
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@ -38,11 +47,7 @@ const ParamScheduler = () => {
|
|||||||
label={t('parameters.scheduler')}
|
label={t('parameters.scheduler')}
|
||||||
selectedItem={scheduler}
|
selectedItem={scheduler}
|
||||||
setSelectedItem={handleChange}
|
setSelectedItem={handleChange}
|
||||||
items={
|
items={allSchedulers}
|
||||||
['img2img', 'unifiedCanvas'].includes(activeTabName)
|
|
||||||
? img2imgSchedulers
|
|
||||||
: schedulers
|
|
||||||
}
|
|
||||||
withCheckIcon
|
withCheckIcon
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -7,6 +7,7 @@ import IAIIconButton, {
|
|||||||
IAIIconButtonProps,
|
IAIIconButtonProps,
|
||||||
} from 'common/components/IAIIconButton';
|
} from 'common/components/IAIIconButton';
|
||||||
import { clampSymmetrySteps } from 'features/parameters/store/generationSlice';
|
import { clampSymmetrySteps } from 'features/parameters/store/generationSlice';
|
||||||
|
import ProgressBar from 'features/system/components/ProgressBar';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
@ -43,7 +44,23 @@ export default function InvokeButton(props: InvokeButton) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box style={{ flexGrow: 4 }}>
|
<Box style={{ flexGrow: 4 }} position="relative">
|
||||||
|
<Box style={{ position: 'relative' }}>
|
||||||
|
{!isReady && (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '0',
|
||||||
|
left: '0',
|
||||||
|
right: '0',
|
||||||
|
height: '100%',
|
||||||
|
overflow: 'clip',
|
||||||
|
borderRadius: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProgressBar />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
{iconButton ? (
|
{iconButton ? (
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
aria-label={t('parameters.invoke')}
|
aria-label={t('parameters.invoke')}
|
||||||
@ -76,5 +93,6 @@ export default function InvokeButton(props: InvokeButton) {
|
|||||||
</IAIButton>
|
</IAIButton>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,151 +0,0 @@
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
|
||||||
import { isFinite, isString } from 'lodash-es';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import useSetBothPrompts from './usePrompt';
|
|
||||||
import { allParametersSet, setSeed } from '../store/generationSlice';
|
|
||||||
import { isImageField } from 'services/types/guards';
|
|
||||||
import { NUMPY_RAND_MAX } from 'app/constants';
|
|
||||||
import { initialImageSelected } from '../store/actions';
|
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
|
||||||
import { useAppToaster } from 'app/components/Toaster';
|
|
||||||
import { ImageDTO } from 'services/api';
|
|
||||||
|
|
||||||
export const useParameters = () => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const toaster = useAppToaster();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const setBothPrompts = useSetBothPrompts();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets prompt with toast
|
|
||||||
*/
|
|
||||||
const recallPrompt = useCallback(
|
|
||||||
(prompt: unknown, negativePrompt?: unknown) => {
|
|
||||||
if (!isString(prompt) || !isString(negativePrompt)) {
|
|
||||||
toaster({
|
|
||||||
title: t('toast.promptNotSet'),
|
|
||||||
description: t('toast.promptNotSetDesc'),
|
|
||||||
status: 'warning',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setBothPrompts(prompt, negativePrompt);
|
|
||||||
toaster({
|
|
||||||
title: t('toast.promptSet'),
|
|
||||||
status: 'info',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[t, toaster, setBothPrompts]
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets seed with toast
|
|
||||||
*/
|
|
||||||
const recallSeed = useCallback(
|
|
||||||
(seed: unknown) => {
|
|
||||||
const s = Number(seed);
|
|
||||||
if (!isFinite(s) || (isFinite(s) && !(s >= 0 && s <= NUMPY_RAND_MAX))) {
|
|
||||||
toaster({
|
|
||||||
title: t('toast.seedNotSet'),
|
|
||||||
description: t('toast.seedNotSetDesc'),
|
|
||||||
status: 'warning',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(setSeed(s));
|
|
||||||
toaster({
|
|
||||||
title: t('toast.seedSet'),
|
|
||||||
status: 'info',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[t, toaster, dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets initial image with toast
|
|
||||||
*/
|
|
||||||
const recallInitialImage = useCallback(
|
|
||||||
async (image: unknown) => {
|
|
||||||
if (!isImageField(image)) {
|
|
||||||
toaster({
|
|
||||||
title: t('toast.initialImageNotSet'),
|
|
||||||
description: t('toast.initialImageNotSetDesc'),
|
|
||||||
status: 'warning',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(initialImageSelected(image.image_name));
|
|
||||||
toaster({
|
|
||||||
title: t('toast.initialImageSet'),
|
|
||||||
status: 'info',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[t, toaster, dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets image as initial image with toast
|
|
||||||
*/
|
|
||||||
const sendToImageToImage = useCallback(
|
|
||||||
(image: ImageDTO) => {
|
|
||||||
dispatch(initialImageSelected(image));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const recallAllParameters = useCallback(
|
|
||||||
(image: ImageDTO | undefined) => {
|
|
||||||
const type = image?.metadata?.type;
|
|
||||||
// not sure what this list should be
|
|
||||||
if (['t2l', 'l2l', 'inpaint'].includes(String(type))) {
|
|
||||||
dispatch(allParametersSet(image));
|
|
||||||
|
|
||||||
if (image?.metadata?.type === 'l2l') {
|
|
||||||
dispatch(setActiveTab('img2img'));
|
|
||||||
} else if (image?.metadata?.type === 't2l') {
|
|
||||||
dispatch(setActiveTab('txt2img'));
|
|
||||||
}
|
|
||||||
|
|
||||||
toaster({
|
|
||||||
title: t('toast.parametersSet'),
|
|
||||||
status: 'success',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
toaster({
|
|
||||||
title: t('toast.parametersNotSet'),
|
|
||||||
description: t('toast.parametersNotSetDesc'),
|
|
||||||
status: 'error',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[t, toaster, dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
recallPrompt,
|
|
||||||
recallSeed,
|
|
||||||
recallInitialImage,
|
|
||||||
sendToImageToImage,
|
|
||||||
recallAllParameters,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,23 +0,0 @@
|
|||||||
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
|
||||||
|
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
|
||||||
import promptToString from 'common/util/promptToString';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
|
||||||
import { setNegativePrompt, setPositivePrompt } from '../store/generationSlice';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
// TECHDEBT: We have two metadata prompt formats and need to handle recalling either of them.
|
|
||||||
// This hook provides a function to do that.
|
|
||||||
const useSetBothPrompts = () => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
return useCallback(
|
|
||||||
(inputPrompt: InvokeAI.Prompt, negativePrompt: InvokeAI.Prompt) => {
|
|
||||||
dispatch(setPositivePrompt(inputPrompt));
|
|
||||||
dispatch(setNegativePrompt(negativePrompt));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useSetBothPrompts;
|
|
@ -0,0 +1,348 @@
|
|||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
modelSelected,
|
||||||
|
setCfgScale,
|
||||||
|
setHeight,
|
||||||
|
setImg2imgStrength,
|
||||||
|
setNegativePrompt,
|
||||||
|
setPositivePrompt,
|
||||||
|
setScheduler,
|
||||||
|
setSeed,
|
||||||
|
setSteps,
|
||||||
|
setWidth,
|
||||||
|
} from '../store/generationSlice';
|
||||||
|
import { isImageField } from 'services/types/guards';
|
||||||
|
import { initialImageSelected } from '../store/actions';
|
||||||
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
|
import { ImageDTO } from 'services/api';
|
||||||
|
import {
|
||||||
|
isValidCfgScale,
|
||||||
|
isValidHeight,
|
||||||
|
isValidModel,
|
||||||
|
isValidNegativePrompt,
|
||||||
|
isValidPositivePrompt,
|
||||||
|
isValidScheduler,
|
||||||
|
isValidSeed,
|
||||||
|
isValidSteps,
|
||||||
|
isValidStrength,
|
||||||
|
isValidWidth,
|
||||||
|
} from '../store/parameterZodSchemas';
|
||||||
|
|
||||||
|
export const useRecallParameters = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const toaster = useAppToaster();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const parameterSetToast = useCallback(() => {
|
||||||
|
toaster({
|
||||||
|
title: t('toast.parameterSet'),
|
||||||
|
status: 'info',
|
||||||
|
duration: 2500,
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
}, [t, toaster]);
|
||||||
|
|
||||||
|
const parameterNotSetToast = useCallback(() => {
|
||||||
|
toaster({
|
||||||
|
title: t('toast.parameterNotSet'),
|
||||||
|
status: 'warning',
|
||||||
|
duration: 2500,
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
}, [t, toaster]);
|
||||||
|
|
||||||
|
const allParameterSetToast = useCallback(() => {
|
||||||
|
toaster({
|
||||||
|
title: t('toast.parametersSet'),
|
||||||
|
status: 'info',
|
||||||
|
duration: 2500,
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
}, [t, toaster]);
|
||||||
|
|
||||||
|
const allParameterNotSetToast = useCallback(() => {
|
||||||
|
toaster({
|
||||||
|
title: t('toast.parametersNotSet'),
|
||||||
|
status: 'warning',
|
||||||
|
duration: 2500,
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
}, [t, toaster]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall both prompts with toast
|
||||||
|
*/
|
||||||
|
const recallBothPrompts = useCallback(
|
||||||
|
(positivePrompt: unknown, negativePrompt: unknown) => {
|
||||||
|
if (
|
||||||
|
isValidPositivePrompt(positivePrompt) ||
|
||||||
|
isValidNegativePrompt(negativePrompt)
|
||||||
|
) {
|
||||||
|
if (isValidPositivePrompt(positivePrompt)) {
|
||||||
|
dispatch(setPositivePrompt(positivePrompt));
|
||||||
|
}
|
||||||
|
if (isValidNegativePrompt(negativePrompt)) {
|
||||||
|
dispatch(setNegativePrompt(negativePrompt));
|
||||||
|
}
|
||||||
|
parameterSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parameterNotSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall positive prompt with toast
|
||||||
|
*/
|
||||||
|
const recallPositivePrompt = useCallback(
|
||||||
|
(positivePrompt: unknown) => {
|
||||||
|
if (!isValidPositivePrompt(positivePrompt)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(setPositivePrompt(positivePrompt));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall negative prompt with toast
|
||||||
|
*/
|
||||||
|
const recallNegativePrompt = useCallback(
|
||||||
|
(negativePrompt: unknown) => {
|
||||||
|
if (!isValidNegativePrompt(negativePrompt)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(setNegativePrompt(negativePrompt));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall seed with toast
|
||||||
|
*/
|
||||||
|
const recallSeed = useCallback(
|
||||||
|
(seed: unknown) => {
|
||||||
|
if (!isValidSeed(seed)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(setSeed(seed));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall CFG scale with toast
|
||||||
|
*/
|
||||||
|
const recallCfgScale = useCallback(
|
||||||
|
(cfgScale: unknown) => {
|
||||||
|
if (!isValidCfgScale(cfgScale)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(setCfgScale(cfgScale));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall model with toast
|
||||||
|
*/
|
||||||
|
const recallModel = useCallback(
|
||||||
|
(model: unknown) => {
|
||||||
|
if (!isValidModel(model)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(modelSelected(model));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall scheduler with toast
|
||||||
|
*/
|
||||||
|
const recallScheduler = useCallback(
|
||||||
|
(scheduler: unknown) => {
|
||||||
|
if (!isValidScheduler(scheduler)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(setScheduler(scheduler));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall steps with toast
|
||||||
|
*/
|
||||||
|
const recallSteps = useCallback(
|
||||||
|
(steps: unknown) => {
|
||||||
|
if (!isValidSteps(steps)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(setSteps(steps));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall width with toast
|
||||||
|
*/
|
||||||
|
const recallWidth = useCallback(
|
||||||
|
(width: unknown) => {
|
||||||
|
if (!isValidWidth(width)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(setWidth(width));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall height with toast
|
||||||
|
*/
|
||||||
|
const recallHeight = useCallback(
|
||||||
|
(height: unknown) => {
|
||||||
|
if (!isValidHeight(height)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(setHeight(height));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recall strength with toast
|
||||||
|
*/
|
||||||
|
const recallStrength = useCallback(
|
||||||
|
(strength: unknown) => {
|
||||||
|
if (!isValidStrength(strength)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(setImg2imgStrength(strength));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets initial image with toast
|
||||||
|
*/
|
||||||
|
const recallInitialImage = useCallback(
|
||||||
|
async (image: unknown) => {
|
||||||
|
if (!isImageField(image)) {
|
||||||
|
parameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(initialImageSelected(image.image_name));
|
||||||
|
parameterSetToast();
|
||||||
|
},
|
||||||
|
[dispatch, parameterSetToast, parameterNotSetToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets image as initial image with toast
|
||||||
|
*/
|
||||||
|
const sendToImageToImage = useCallback(
|
||||||
|
(image: ImageDTO) => {
|
||||||
|
dispatch(initialImageSelected(image));
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const recallAllParameters = useCallback(
|
||||||
|
(image: ImageDTO | undefined) => {
|
||||||
|
if (!image || !image.metadata) {
|
||||||
|
allParameterNotSetToast();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
cfg_scale,
|
||||||
|
height,
|
||||||
|
model,
|
||||||
|
positive_conditioning,
|
||||||
|
negative_conditioning,
|
||||||
|
scheduler,
|
||||||
|
seed,
|
||||||
|
steps,
|
||||||
|
width,
|
||||||
|
strength,
|
||||||
|
clip,
|
||||||
|
extra,
|
||||||
|
latents,
|
||||||
|
unet,
|
||||||
|
vae,
|
||||||
|
} = image.metadata;
|
||||||
|
|
||||||
|
if (isValidCfgScale(cfg_scale)) {
|
||||||
|
dispatch(setCfgScale(cfg_scale));
|
||||||
|
}
|
||||||
|
if (isValidModel(model)) {
|
||||||
|
dispatch(modelSelected(model));
|
||||||
|
}
|
||||||
|
if (isValidPositivePrompt(positive_conditioning)) {
|
||||||
|
dispatch(setPositivePrompt(positive_conditioning));
|
||||||
|
}
|
||||||
|
if (isValidNegativePrompt(negative_conditioning)) {
|
||||||
|
dispatch(setNegativePrompt(negative_conditioning));
|
||||||
|
}
|
||||||
|
if (isValidScheduler(scheduler)) {
|
||||||
|
dispatch(setScheduler(scheduler));
|
||||||
|
}
|
||||||
|
if (isValidSeed(seed)) {
|
||||||
|
dispatch(setSeed(seed));
|
||||||
|
}
|
||||||
|
if (isValidSteps(steps)) {
|
||||||
|
dispatch(setSteps(steps));
|
||||||
|
}
|
||||||
|
if (isValidWidth(width)) {
|
||||||
|
dispatch(setWidth(width));
|
||||||
|
}
|
||||||
|
if (isValidHeight(height)) {
|
||||||
|
dispatch(setHeight(height));
|
||||||
|
}
|
||||||
|
if (isValidStrength(strength)) {
|
||||||
|
dispatch(setImg2imgStrength(strength));
|
||||||
|
}
|
||||||
|
|
||||||
|
allParameterSetToast();
|
||||||
|
},
|
||||||
|
[allParameterNotSetToast, allParameterSetToast, dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
recallBothPrompts,
|
||||||
|
recallPositivePrompt,
|
||||||
|
recallNegativePrompt,
|
||||||
|
recallSeed,
|
||||||
|
recallInitialImage,
|
||||||
|
recallCfgScale,
|
||||||
|
recallModel,
|
||||||
|
recallScheduler,
|
||||||
|
recallSteps,
|
||||||
|
recallWidth,
|
||||||
|
recallHeight,
|
||||||
|
recallStrength,
|
||||||
|
recallAllParameters,
|
||||||
|
sendToImageToImage,
|
||||||
|
};
|
||||||
|
};
|
@ -1,43 +1,53 @@
|
|||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
import { clamp, sortBy } from 'lodash-es';
|
||||||
import promptToString from 'common/util/promptToString';
|
|
||||||
import { clamp, sample } from 'lodash-es';
|
|
||||||
import { setAllParametersReducer } from './setAllParametersReducer';
|
|
||||||
import { receivedModels } from 'services/thunks/model';
|
import { receivedModels } from 'services/thunks/model';
|
||||||
import { Scheduler } from 'app/constants';
|
import { Scheduler } from 'app/constants';
|
||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
|
import { configChanged } from 'features/system/store/configSlice';
|
||||||
|
import {
|
||||||
|
CfgScaleParam,
|
||||||
|
HeightParam,
|
||||||
|
ModelParam,
|
||||||
|
NegativePromptParam,
|
||||||
|
PositivePromptParam,
|
||||||
|
SchedulerParam,
|
||||||
|
SeedParam,
|
||||||
|
StepsParam,
|
||||||
|
StrengthParam,
|
||||||
|
WidthParam,
|
||||||
|
} from './parameterZodSchemas';
|
||||||
|
|
||||||
export interface GenerationState {
|
export interface GenerationState {
|
||||||
cfgScale: number;
|
cfgScale: CfgScaleParam;
|
||||||
height: number;
|
height: HeightParam;
|
||||||
img2imgStrength: number;
|
img2imgStrength: StrengthParam;
|
||||||
infillMethod: string;
|
infillMethod: string;
|
||||||
initialImage?: ImageDTO;
|
initialImage?: ImageDTO;
|
||||||
iterations: number;
|
iterations: number;
|
||||||
perlin: number;
|
perlin: number;
|
||||||
positivePrompt: string;
|
positivePrompt: PositivePromptParam;
|
||||||
negativePrompt: string;
|
negativePrompt: NegativePromptParam;
|
||||||
scheduler: Scheduler;
|
scheduler: SchedulerParam;
|
||||||
seamBlur: number;
|
seamBlur: number;
|
||||||
seamSize: number;
|
seamSize: number;
|
||||||
seamSteps: number;
|
seamSteps: number;
|
||||||
seamStrength: number;
|
seamStrength: number;
|
||||||
seed: number;
|
seed: SeedParam;
|
||||||
seedWeights: string;
|
seedWeights: string;
|
||||||
shouldFitToWidthHeight: boolean;
|
shouldFitToWidthHeight: boolean;
|
||||||
shouldGenerateVariations: boolean;
|
shouldGenerateVariations: boolean;
|
||||||
shouldRandomizeSeed: boolean;
|
shouldRandomizeSeed: boolean;
|
||||||
shouldUseNoiseSettings: boolean;
|
shouldUseNoiseSettings: boolean;
|
||||||
steps: number;
|
steps: StepsParam;
|
||||||
threshold: number;
|
threshold: number;
|
||||||
tileSize: number;
|
tileSize: number;
|
||||||
variationAmount: number;
|
variationAmount: number;
|
||||||
width: number;
|
width: WidthParam;
|
||||||
shouldUseSymmetry: boolean;
|
shouldUseSymmetry: boolean;
|
||||||
horizontalSymmetrySteps: number;
|
horizontalSymmetrySteps: number;
|
||||||
verticalSymmetrySteps: number;
|
verticalSymmetrySteps: number;
|
||||||
model: string;
|
model: ModelParam;
|
||||||
shouldUseSeamless: boolean;
|
shouldUseSeamless: boolean;
|
||||||
seamlessXAxis: boolean;
|
seamlessXAxis: boolean;
|
||||||
seamlessYAxis: boolean;
|
seamlessYAxis: boolean;
|
||||||
@ -83,27 +93,11 @@ export const generationSlice = createSlice({
|
|||||||
name: 'generation',
|
name: 'generation',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setPositivePrompt: (
|
setPositivePrompt: (state, action: PayloadAction<string>) => {
|
||||||
state,
|
state.positivePrompt = action.payload;
|
||||||
action: PayloadAction<string | InvokeAI.Prompt>
|
|
||||||
) => {
|
|
||||||
const newPrompt = action.payload;
|
|
||||||
if (typeof newPrompt === 'string') {
|
|
||||||
state.positivePrompt = newPrompt;
|
|
||||||
} else {
|
|
||||||
state.positivePrompt = promptToString(newPrompt);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setNegativePrompt: (
|
setNegativePrompt: (state, action: PayloadAction<string>) => {
|
||||||
state,
|
state.negativePrompt = action.payload;
|
||||||
action: PayloadAction<string | InvokeAI.Prompt>
|
|
||||||
) => {
|
|
||||||
const newPrompt = action.payload;
|
|
||||||
if (typeof newPrompt === 'string') {
|
|
||||||
state.negativePrompt = newPrompt;
|
|
||||||
} else {
|
|
||||||
state.negativePrompt = promptToString(newPrompt);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setIterations: (state, action: PayloadAction<number>) => {
|
setIterations: (state, action: PayloadAction<number>) => {
|
||||||
state.iterations = action.payload;
|
state.iterations = action.payload;
|
||||||
@ -174,7 +168,6 @@ export const generationSlice = createSlice({
|
|||||||
state.shouldGenerateVariations = true;
|
state.shouldGenerateVariations = true;
|
||||||
state.variationAmount = 0;
|
state.variationAmount = 0;
|
||||||
},
|
},
|
||||||
allParametersSet: setAllParametersReducer,
|
|
||||||
resetParametersState: (state) => {
|
resetParametersState: (state) => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@ -227,10 +220,15 @@ export const generationSlice = createSlice({
|
|||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder.addCase(receivedModels.fulfilled, (state, action) => {
|
builder.addCase(receivedModels.fulfilled, (state, action) => {
|
||||||
if (!state.model) {
|
if (!state.model) {
|
||||||
const randomModel = sample(action.payload);
|
const firstModel = sortBy(action.payload, 'name')[0];
|
||||||
if (randomModel) {
|
state.model = firstModel.name;
|
||||||
state.model = randomModel.name;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.addCase(configChanged, (state, action) => {
|
||||||
|
const defaultModel = action.payload.sd?.defaultModel;
|
||||||
|
if (defaultModel && !state.model) {
|
||||||
|
state.model = defaultModel;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -273,7 +271,6 @@ export const {
|
|||||||
setSeamless,
|
setSeamless,
|
||||||
setSeamlessXAxis,
|
setSeamlessXAxis,
|
||||||
setSeamlessYAxis,
|
setSeamlessYAxis,
|
||||||
allParametersSet,
|
|
||||||
} = generationSlice.actions;
|
} = generationSlice.actions;
|
||||||
|
|
||||||
export default generationSlice.reducer;
|
export default generationSlice.reducer;
|
||||||
|
@ -0,0 +1,156 @@
|
|||||||
|
import { NUMPY_RAND_MAX, SCHEDULERS } from 'app/constants';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These zod schemas should match the pydantic node schemas.
|
||||||
|
*
|
||||||
|
* Parameters only need schemas if we want to recall them from metadata.
|
||||||
|
*
|
||||||
|
* Each parameter needs:
|
||||||
|
* - a zod schema
|
||||||
|
* - a type alias, inferred from the zod schema
|
||||||
|
* - a combo validation/type guard function, which returns true if the value is valid
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for positive prompt parameter
|
||||||
|
*/
|
||||||
|
export const zPositivePrompt = z.string();
|
||||||
|
/**
|
||||||
|
* Type alias for positive prompt parameter, inferred from its zod schema
|
||||||
|
*/
|
||||||
|
export type PositivePromptParam = z.infer<typeof zPositivePrompt>;
|
||||||
|
/**
|
||||||
|
* Validates/type-guards a value as a positive prompt parameter
|
||||||
|
*/
|
||||||
|
export const isValidPositivePrompt = (
|
||||||
|
val: unknown
|
||||||
|
): val is PositivePromptParam => zPositivePrompt.safeParse(val).success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for negative prompt parameter
|
||||||
|
*/
|
||||||
|
export const zNegativePrompt = z.string();
|
||||||
|
/**
|
||||||
|
* Type alias for negative prompt parameter, inferred from its zod schema
|
||||||
|
*/
|
||||||
|
export type NegativePromptParam = z.infer<typeof zNegativePrompt>;
|
||||||
|
/**
|
||||||
|
* Validates/type-guards a value as a negative prompt parameter
|
||||||
|
*/
|
||||||
|
export const isValidNegativePrompt = (
|
||||||
|
val: unknown
|
||||||
|
): val is NegativePromptParam => zNegativePrompt.safeParse(val).success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for steps parameter
|
||||||
|
*/
|
||||||
|
export const zSteps = z.number().int().min(1);
|
||||||
|
/**
|
||||||
|
* Type alias for steps parameter, inferred from its zod schema
|
||||||
|
*/
|
||||||
|
export type StepsParam = z.infer<typeof zSteps>;
|
||||||
|
/**
|
||||||
|
* Validates/type-guards a value as a steps parameter
|
||||||
|
*/
|
||||||
|
export const isValidSteps = (val: unknown): val is StepsParam =>
|
||||||
|
zSteps.safeParse(val).success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for CFG scale parameter
|
||||||
|
*/
|
||||||
|
export const zCfgScale = z.number().min(1);
|
||||||
|
/**
|
||||||
|
* Type alias for CFG scale parameter, inferred from its zod schema
|
||||||
|
*/
|
||||||
|
export type CfgScaleParam = z.infer<typeof zCfgScale>;
|
||||||
|
/**
|
||||||
|
* Validates/type-guards a value as a CFG scale parameter
|
||||||
|
*/
|
||||||
|
export const isValidCfgScale = (val: unknown): val is CfgScaleParam =>
|
||||||
|
zCfgScale.safeParse(val).success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for scheduler parameter
|
||||||
|
*/
|
||||||
|
export const zScheduler = z.enum(SCHEDULERS);
|
||||||
|
/**
|
||||||
|
* Type alias for scheduler parameter, inferred from its zod schema
|
||||||
|
*/
|
||||||
|
export type SchedulerParam = z.infer<typeof zScheduler>;
|
||||||
|
/**
|
||||||
|
* Validates/type-guards a value as a scheduler parameter
|
||||||
|
*/
|
||||||
|
export const isValidScheduler = (val: unknown): val is SchedulerParam =>
|
||||||
|
zScheduler.safeParse(val).success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for seed parameter
|
||||||
|
*/
|
||||||
|
export const zSeed = z.number().int().min(0).max(NUMPY_RAND_MAX);
|
||||||
|
/**
|
||||||
|
* Type alias for seed parameter, inferred from its zod schema
|
||||||
|
*/
|
||||||
|
export type SeedParam = z.infer<typeof zSeed>;
|
||||||
|
/**
|
||||||
|
* Validates/type-guards a value as a seed parameter
|
||||||
|
*/
|
||||||
|
export const isValidSeed = (val: unknown): val is SeedParam =>
|
||||||
|
zSeed.safeParse(val).success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for width parameter
|
||||||
|
*/
|
||||||
|
export const zWidth = z.number().multipleOf(8).min(64);
|
||||||
|
/**
|
||||||
|
* Type alias for width parameter, inferred from its zod schema
|
||||||
|
*/
|
||||||
|
export type WidthParam = z.infer<typeof zWidth>;
|
||||||
|
/**
|
||||||
|
* Validates/type-guards a value as a width parameter
|
||||||
|
*/
|
||||||
|
export const isValidWidth = (val: unknown): val is WidthParam =>
|
||||||
|
zWidth.safeParse(val).success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for height parameter
|
||||||
|
*/
|
||||||
|
export const zHeight = z.number().multipleOf(8).min(64);
|
||||||
|
/**
|
||||||
|
* Type alias for height parameter, inferred from its zod schema
|
||||||
|
*/
|
||||||
|
export type HeightParam = z.infer<typeof zHeight>;
|
||||||
|
/**
|
||||||
|
* Validates/type-guards a value as a height parameter
|
||||||
|
*/
|
||||||
|
export const isValidHeight = (val: unknown): val is HeightParam =>
|
||||||
|
zHeight.safeParse(val).success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for model parameter
|
||||||
|
* TODO: Make this a dynamically generated enum?
|
||||||
|
*/
|
||||||
|
export const zModel = z.string();
|
||||||
|
/**
|
||||||
|
* Type alias for model parameter, inferred from its zod schema
|
||||||
|
*/
|
||||||
|
export type ModelParam = z.infer<typeof zModel>;
|
||||||
|
/**
|
||||||
|
* Validates/type-guards a value as a model parameter
|
||||||
|
*/
|
||||||
|
export const isValidModel = (val: unknown): val is ModelParam =>
|
||||||
|
zModel.safeParse(val).success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zod schema for l2l strength parameter
|
||||||
|
*/
|
||||||
|
export const zStrength = z.number().min(0).max(1);
|
||||||
|
/**
|
||||||
|
* Type alias for l2l strength parameter, inferred from its zod schema
|
||||||
|
*/
|
||||||
|
export type StrengthParam = z.infer<typeof zStrength>;
|
||||||
|
/**
|
||||||
|
* Validates/type-guards a value as a l2l strength parameter
|
||||||
|
*/
|
||||||
|
export const isValidStrength = (val: unknown): val is StrengthParam =>
|
||||||
|
zStrength.safeParse(val).success;
|
@ -1,77 +0,0 @@
|
|||||||
import { Draft, PayloadAction } from '@reduxjs/toolkit';
|
|
||||||
import { GenerationState } from './generationSlice';
|
|
||||||
import { ImageDTO, ImageToImageInvocation } from 'services/api';
|
|
||||||
import { isScheduler } from 'app/constants';
|
|
||||||
|
|
||||||
export const setAllParametersReducer = (
|
|
||||||
state: Draft<GenerationState>,
|
|
||||||
action: PayloadAction<ImageDTO | undefined>
|
|
||||||
) => {
|
|
||||||
const metadata = action.payload?.metadata;
|
|
||||||
|
|
||||||
if (!metadata) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// not sure what this list should be
|
|
||||||
if (
|
|
||||||
metadata.type === 't2l' ||
|
|
||||||
metadata.type === 'l2l' ||
|
|
||||||
metadata.type === 'inpaint'
|
|
||||||
) {
|
|
||||||
const {
|
|
||||||
cfg_scale,
|
|
||||||
height,
|
|
||||||
model,
|
|
||||||
positive_conditioning,
|
|
||||||
negative_conditioning,
|
|
||||||
scheduler,
|
|
||||||
seed,
|
|
||||||
steps,
|
|
||||||
width,
|
|
||||||
} = metadata;
|
|
||||||
|
|
||||||
if (cfg_scale !== undefined) {
|
|
||||||
state.cfgScale = Number(cfg_scale);
|
|
||||||
}
|
|
||||||
if (height !== undefined) {
|
|
||||||
state.height = Number(height);
|
|
||||||
}
|
|
||||||
if (model !== undefined) {
|
|
||||||
state.model = String(model);
|
|
||||||
}
|
|
||||||
if (positive_conditioning !== undefined) {
|
|
||||||
state.positivePrompt = String(positive_conditioning);
|
|
||||||
}
|
|
||||||
if (negative_conditioning !== undefined) {
|
|
||||||
state.negativePrompt = String(negative_conditioning);
|
|
||||||
}
|
|
||||||
if (scheduler !== undefined) {
|
|
||||||
const schedulerString = String(scheduler);
|
|
||||||
if (isScheduler(schedulerString)) {
|
|
||||||
state.scheduler = schedulerString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (seed !== undefined) {
|
|
||||||
state.seed = Number(seed);
|
|
||||||
state.shouldRandomizeSeed = false;
|
|
||||||
}
|
|
||||||
if (steps !== undefined) {
|
|
||||||
state.steps = Number(steps);
|
|
||||||
}
|
|
||||||
if (width !== undefined) {
|
|
||||||
state.width = Number(width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadata.type === 'l2l') {
|
|
||||||
const { fit, image } = metadata as ImageToImageInvocation;
|
|
||||||
|
|
||||||
if (fit !== undefined) {
|
|
||||||
state.shouldFitToWidthHeight = Boolean(fit);
|
|
||||||
}
|
|
||||||
// if (image !== undefined) {
|
|
||||||
// state.initialImage = image;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
};
|
|
@ -4,19 +4,33 @@ import { isEqual } from 'lodash-es';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectModelsById, selectModelsIds } from '../store/modelSlice';
|
import {
|
||||||
|
selectModelsAll,
|
||||||
|
selectModelsById,
|
||||||
|
selectModelsIds,
|
||||||
|
} from '../store/modelSlice';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { modelSelected } from 'features/parameters/store/generationSlice';
|
import { modelSelected } from 'features/parameters/store/generationSlice';
|
||||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||||
import IAICustomSelect from 'common/components/IAICustomSelect';
|
import IAICustomSelect, {
|
||||||
|
ItemTooltips,
|
||||||
|
} from 'common/components/IAICustomSelect';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
[(state: RootState) => state, generationSelector],
|
[(state: RootState) => state, generationSelector],
|
||||||
(state, generation) => {
|
(state, generation) => {
|
||||||
const selectedModel = selectModelsById(state, generation.model);
|
const selectedModel = selectModelsById(state, generation.model);
|
||||||
const allModelNames = selectModelsIds(state).map((id) => String(id));
|
const allModelNames = selectModelsIds(state).map((id) => String(id));
|
||||||
|
const allModelTooltips = selectModelsAll(state).reduce(
|
||||||
|
(allModelTooltips, model) => {
|
||||||
|
allModelTooltips[model.name] = model.description ?? '';
|
||||||
|
return allModelTooltips;
|
||||||
|
},
|
||||||
|
{} as ItemTooltips
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
allModelNames,
|
allModelNames,
|
||||||
|
allModelTooltips,
|
||||||
selectedModel,
|
selectedModel,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -30,7 +44,8 @@ const selector = createSelector(
|
|||||||
const ModelSelect = () => {
|
const ModelSelect = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { allModelNames, selectedModel } = useAppSelector(selector);
|
const { allModelNames, allModelTooltips, selectedModel } =
|
||||||
|
useAppSelector(selector);
|
||||||
const handleChangeModel = useCallback(
|
const handleChangeModel = useCallback(
|
||||||
(v: string | null | undefined) => {
|
(v: string | null | undefined) => {
|
||||||
if (!v) {
|
if (!v) {
|
||||||
@ -46,6 +61,7 @@ const ModelSelect = () => {
|
|||||||
label={t('modelManager.model')}
|
label={t('modelManager.model')}
|
||||||
tooltip={selectedModel?.description}
|
tooltip={selectedModel?.description}
|
||||||
items={allModelNames}
|
items={allModelNames}
|
||||||
|
itemTooltips={allModelTooltips}
|
||||||
selectedItem={selectedModel?.name ?? ''}
|
selectedItem={selectedModel?.name ?? ''}
|
||||||
setSelectedItem={handleChangeModel}
|
setSelectedItem={handleChangeModel}
|
||||||
withCheckIcon={true}
|
withCheckIcon={true}
|
||||||
|
@ -5,7 +5,6 @@ import { SystemState } from 'features/system/store/systemSlice';
|
|||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PROGRESS_BAR_THICKNESS } from 'theme/util/constants';
|
|
||||||
import { systemSelector } from '../store/systemSelectors';
|
import { systemSelector } from '../store/systemSelectors';
|
||||||
|
|
||||||
const progressBarSelector = createSelector(
|
const progressBarSelector = createSelector(
|
||||||
@ -35,7 +34,7 @@ const ProgressBar = () => {
|
|||||||
value={value}
|
value={value}
|
||||||
aria-label={t('accessibility.invokeProgressBar')}
|
aria-label={t('accessibility.invokeProgressBar')}
|
||||||
isIndeterminate={isProcessing && !currentStatusHasSteps}
|
isIndeterminate={isProcessing && !currentStatusHasSteps}
|
||||||
height={PROGRESS_BAR_THICKNESS}
|
height="full"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -35,7 +35,13 @@ import {
|
|||||||
} from 'features/ui/store/uiSlice';
|
} from 'features/ui/store/uiSlice';
|
||||||
import { UIState } from 'features/ui/store/uiTypes';
|
import { UIState } from 'features/ui/store/uiTypes';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { ChangeEvent, cloneElement, ReactElement, useCallback } from 'react';
|
import {
|
||||||
|
ChangeEvent,
|
||||||
|
cloneElement,
|
||||||
|
ReactElement,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { VALID_LOG_LEVELS } from 'app/logging/useLogger';
|
import { VALID_LOG_LEVELS } from 'app/logging/useLogger';
|
||||||
import { LogLevelName } from 'roarr';
|
import { LogLevelName } from 'roarr';
|
||||||
@ -85,15 +91,33 @@ const modalSectionStyles: ChakraProps['sx'] = {
|
|||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ConfigOptions = {
|
||||||
|
shouldShowDeveloperSettings: boolean;
|
||||||
|
shouldShowResetWebUiText: boolean;
|
||||||
|
shouldShowBetaLayout: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
type SettingsModalProps = {
|
type SettingsModalProps = {
|
||||||
/* The button to open the Settings Modal */
|
/* The button to open the Settings Modal */
|
||||||
children: ReactElement;
|
children: ReactElement;
|
||||||
|
config?: ConfigOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsModal = ({ children }: SettingsModalProps) => {
|
const SettingsModal = ({ children, config }: SettingsModalProps) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const shouldShowBetaLayout = config?.shouldShowBetaLayout ?? true;
|
||||||
|
const shouldShowDeveloperSettings =
|
||||||
|
config?.shouldShowDeveloperSettings ?? true;
|
||||||
|
const shouldShowResetWebUiText = config?.shouldShowResetWebUiText ?? true;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!shouldShowDeveloperSettings) {
|
||||||
|
dispatch(shouldLogToConsoleChanged(false));
|
||||||
|
}
|
||||||
|
}, [shouldShowDeveloperSettings, dispatch]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen: isSettingsModalOpen,
|
isOpen: isSettingsModalOpen,
|
||||||
onOpen: onSettingsModalOpen,
|
onOpen: onSettingsModalOpen,
|
||||||
@ -189,6 +213,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
|||||||
dispatch(setShouldDisplayGuides(e.target.checked))
|
dispatch(setShouldDisplayGuides(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
{shouldShowBetaLayout && (
|
||||||
<IAISwitch
|
<IAISwitch
|
||||||
label={t('settings.useCanvasBeta')}
|
label={t('settings.useCanvasBeta')}
|
||||||
isChecked={shouldUseCanvasBetaLayout}
|
isChecked={shouldUseCanvasBetaLayout}
|
||||||
@ -196,6 +221,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
|||||||
dispatch(setShouldUseCanvasBetaLayout(e.target.checked))
|
dispatch(setShouldUseCanvasBetaLayout(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
<IAISwitch
|
<IAISwitch
|
||||||
label={t('settings.useSlidersForAll')}
|
label={t('settings.useSlidersForAll')}
|
||||||
isChecked={shouldUseSliders}
|
isChecked={shouldUseSliders}
|
||||||
@ -221,6 +247,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
{shouldShowDeveloperSettings && (
|
||||||
<Flex sx={modalSectionStyles}>
|
<Flex sx={modalSectionStyles}>
|
||||||
<Heading size="sm">{t('settings.developer')}</Heading>
|
<Heading size="sm">{t('settings.developer')}</Heading>
|
||||||
<IAISwitch
|
<IAISwitch
|
||||||
@ -245,14 +272,19 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
)}
|
||||||
|
|
||||||
<Flex sx={modalSectionStyles}>
|
<Flex sx={modalSectionStyles}>
|
||||||
<Heading size="sm">{t('settings.resetWebUI')}</Heading>
|
<Heading size="sm">{t('settings.resetWebUI')}</Heading>
|
||||||
<IAIButton colorScheme="error" onClick={handleClickResetWebUI}>
|
<IAIButton colorScheme="error" onClick={handleClickResetWebUI}>
|
||||||
{t('settings.resetWebUI')}
|
{t('settings.resetWebUI')}
|
||||||
</IAIButton>
|
</IAIButton>
|
||||||
|
{shouldShowResetWebUiText && (
|
||||||
|
<>
|
||||||
<Text>{t('settings.resetWebUIDesc1')}</Text>
|
<Text>{t('settings.resetWebUIDesc1')}</Text>
|
||||||
<Text>{t('settings.resetWebUIDesc2')}</Text>
|
<Text>{t('settings.resetWebUIDesc2')}</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
@ -7,11 +7,11 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
|||||||
import ParamIterations from 'features/parameters/components/Parameters/Core/ParamIterations';
|
import ParamIterations from 'features/parameters/components/Parameters/Core/ParamIterations';
|
||||||
import ParamSteps from 'features/parameters/components/Parameters/Core/ParamSteps';
|
import ParamSteps from 'features/parameters/components/Parameters/Core/ParamSteps';
|
||||||
import ParamCFGScale from 'features/parameters/components/Parameters/Core/ParamCFGScale';
|
import ParamCFGScale from 'features/parameters/components/Parameters/Core/ParamCFGScale';
|
||||||
import ParamWidth from 'features/parameters/components/Parameters/Core/ParamWidth';
|
|
||||||
import ParamHeight from 'features/parameters/components/Parameters/Core/ParamHeight';
|
|
||||||
import ImageToImageStrength from 'features/parameters/components/Parameters/ImageToImage/ImageToImageStrength';
|
import ImageToImageStrength from 'features/parameters/components/Parameters/ImageToImage/ImageToImageStrength';
|
||||||
import ImageToImageFit from 'features/parameters/components/Parameters/ImageToImage/ImageToImageFit';
|
import ImageToImageFit from 'features/parameters/components/Parameters/ImageToImage/ImageToImageFit';
|
||||||
import ParamSchedulerAndModel from 'features/parameters/components/Parameters/Core/ParamSchedulerAndModel';
|
import ParamSchedulerAndModel from 'features/parameters/components/Parameters/Core/ParamSchedulerAndModel';
|
||||||
|
import ParamBoundingBoxWidth from 'features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxWidth';
|
||||||
|
import ParamBoundingBoxHeight from 'features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxHeight';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
uiSelector,
|
uiSelector,
|
||||||
@ -41,8 +41,8 @@ const UnifiedCanvasCoreParameters = () => {
|
|||||||
<ParamIterations />
|
<ParamIterations />
|
||||||
<ParamSteps />
|
<ParamSteps />
|
||||||
<ParamCFGScale />
|
<ParamCFGScale />
|
||||||
<ParamWidth />
|
<ParamBoundingBoxWidth />
|
||||||
<ParamHeight />
|
<ParamBoundingBoxHeight />
|
||||||
<ImageToImageStrength />
|
<ImageToImageStrength />
|
||||||
<ImageToImageFit />
|
<ImageToImageFit />
|
||||||
<ParamSchedulerAndModel />
|
<ParamSchedulerAndModel />
|
||||||
@ -55,8 +55,8 @@ const UnifiedCanvasCoreParameters = () => {
|
|||||||
<ParamCFGScale />
|
<ParamCFGScale />
|
||||||
</Flex>
|
</Flex>
|
||||||
<ParamSchedulerAndModel />
|
<ParamSchedulerAndModel />
|
||||||
<ParamWidth />
|
<ParamBoundingBoxWidth />
|
||||||
<ParamHeight />
|
<ParamBoundingBoxHeight />
|
||||||
<ImageToImageStrength />
|
<ImageToImageStrength />
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
@ -2,7 +2,6 @@ import ProcessButtons from 'features/parameters/components/ProcessButtons/Proces
|
|||||||
import ParamSeedCollapse from 'features/parameters/components/Parameters/Seed/ParamSeedCollapse';
|
import ParamSeedCollapse from 'features/parameters/components/Parameters/Seed/ParamSeedCollapse';
|
||||||
import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
||||||
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
||||||
import ParamBoundingBoxCollapse from 'features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxCollapse';
|
|
||||||
import ParamInfillAndScalingCollapse from 'features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamInfillAndScalingCollapse';
|
import ParamInfillAndScalingCollapse from 'features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamInfillAndScalingCollapse';
|
||||||
import ParamSeamCorrectionCollapse from 'features/parameters/components/Parameters/Canvas/SeamCorrection/ParamSeamCorrectionCollapse';
|
import ParamSeamCorrectionCollapse from 'features/parameters/components/Parameters/Canvas/SeamCorrection/ParamSeamCorrectionCollapse';
|
||||||
import UnifiedCanvasCoreParameters from './UnifiedCanvasCoreParameters';
|
import UnifiedCanvasCoreParameters from './UnifiedCanvasCoreParameters';
|
||||||
@ -20,7 +19,6 @@ const UnifiedCanvasParameters = () => {
|
|||||||
<ParamSeedCollapse />
|
<ParamSeedCollapse />
|
||||||
<ParamVariationCollapse />
|
<ParamVariationCollapse />
|
||||||
<ParamSymmetryCollapse />
|
<ParamSymmetryCollapse />
|
||||||
<ParamBoundingBoxCollapse />
|
|
||||||
<ParamSeamCorrectionCollapse />
|
<ParamSeamCorrectionCollapse />
|
||||||
<ParamInfillAndScalingCollapse />
|
<ParamInfillAndScalingCollapse />
|
||||||
</>
|
</>
|
||||||
|
@ -19,7 +19,7 @@ export const initialUIState: UIState = {
|
|||||||
shouldPinGallery: true,
|
shouldPinGallery: true,
|
||||||
shouldShowGallery: true,
|
shouldShowGallery: true,
|
||||||
shouldHidePreview: false,
|
shouldHidePreview: false,
|
||||||
shouldShowProgressInViewer: false,
|
shouldShowProgressInViewer: true,
|
||||||
schedulers: SCHEDULERS,
|
schedulers: SCHEDULERS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,6 +49,8 @@ export type { ImageOutput } from './models/ImageOutput';
|
|||||||
export type { ImagePasteInvocation } from './models/ImagePasteInvocation';
|
export type { ImagePasteInvocation } from './models/ImagePasteInvocation';
|
||||||
export type { ImageProcessorInvocation } from './models/ImageProcessorInvocation';
|
export type { ImageProcessorInvocation } from './models/ImageProcessorInvocation';
|
||||||
export type { ImageRecordChanges } from './models/ImageRecordChanges';
|
export type { ImageRecordChanges } from './models/ImageRecordChanges';
|
||||||
|
export type { ImageResizeInvocation } from './models/ImageResizeInvocation';
|
||||||
|
export type { ImageScaleInvocation } from './models/ImageScaleInvocation';
|
||||||
export type { ImageToImageInvocation } from './models/ImageToImageInvocation';
|
export type { ImageToImageInvocation } from './models/ImageToImageInvocation';
|
||||||
export type { ImageToLatentsInvocation } from './models/ImageToLatentsInvocation';
|
export type { ImageToLatentsInvocation } from './models/ImageToLatentsInvocation';
|
||||||
export type { ImageUrlsDTO } from './models/ImageUrlsDTO';
|
export type { ImageUrlsDTO } from './models/ImageUrlsDTO';
|
||||||
|
@ -22,6 +22,8 @@ import type { ImageLerpInvocation } from './ImageLerpInvocation';
|
|||||||
import type { ImageMultiplyInvocation } from './ImageMultiplyInvocation';
|
import type { ImageMultiplyInvocation } from './ImageMultiplyInvocation';
|
||||||
import type { ImagePasteInvocation } from './ImagePasteInvocation';
|
import type { ImagePasteInvocation } from './ImagePasteInvocation';
|
||||||
import type { ImageProcessorInvocation } from './ImageProcessorInvocation';
|
import type { ImageProcessorInvocation } from './ImageProcessorInvocation';
|
||||||
|
import type { ImageResizeInvocation } from './ImageResizeInvocation';
|
||||||
|
import type { ImageScaleInvocation } from './ImageScaleInvocation';
|
||||||
import type { ImageToImageInvocation } from './ImageToImageInvocation';
|
import type { ImageToImageInvocation } from './ImageToImageInvocation';
|
||||||
import type { ImageToLatentsInvocation } from './ImageToLatentsInvocation';
|
import type { ImageToLatentsInvocation } from './ImageToLatentsInvocation';
|
||||||
import type { InfillColorInvocation } from './InfillColorInvocation';
|
import type { InfillColorInvocation } from './InfillColorInvocation';
|
||||||
@ -67,7 +69,7 @@ export type Graph = {
|
|||||||
/**
|
/**
|
||||||
* The nodes in this graph
|
* The nodes in this graph
|
||||||
*/
|
*/
|
||||||
nodes?: Record<string, (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageprocessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation)>;
|
nodes?: Record<string, (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageResizeInvocation | ImageScaleInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageprocessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation)>;
|
||||||
/**
|
/**
|
||||||
* The connections between nodes and their fields in this graph
|
* The connections between nodes and their fields in this graph
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
import type { ImageField } from './ImageField';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizes an image to specific dimensions
|
||||||
|
*/
|
||||||
|
export type ImageResizeInvocation = {
|
||||||
|
/**
|
||||||
|
* The id of this node. Must be unique among all nodes.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* Whether or not this node is an intermediate node.
|
||||||
|
*/
|
||||||
|
is_intermediate?: boolean;
|
||||||
|
type?: 'img_resize';
|
||||||
|
/**
|
||||||
|
* The image to resize
|
||||||
|
*/
|
||||||
|
image?: ImageField;
|
||||||
|
/**
|
||||||
|
* The width to resize to (px)
|
||||||
|
*/
|
||||||
|
width: number;
|
||||||
|
/**
|
||||||
|
* The height to resize to (px)
|
||||||
|
*/
|
||||||
|
height: number;
|
||||||
|
/**
|
||||||
|
* The resampling mode
|
||||||
|
*/
|
||||||
|
resample_mode?: 'nearest' | 'box' | 'bilinear' | 'hamming' | 'bicubic' | 'lanczos';
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,33 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
import type { ImageField } from './ImageField';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales an image by a factor
|
||||||
|
*/
|
||||||
|
export type ImageScaleInvocation = {
|
||||||
|
/**
|
||||||
|
* The id of this node. Must be unique among all nodes.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* Whether or not this node is an intermediate node.
|
||||||
|
*/
|
||||||
|
is_intermediate?: boolean;
|
||||||
|
type?: 'img_scale';
|
||||||
|
/**
|
||||||
|
* The image to scale
|
||||||
|
*/
|
||||||
|
image?: ImageField;
|
||||||
|
/**
|
||||||
|
* The factor by which to scale the image
|
||||||
|
*/
|
||||||
|
scale_factor: number;
|
||||||
|
/**
|
||||||
|
* The resampling mode
|
||||||
|
*/
|
||||||
|
resample_mode?: 'nearest' | 'box' | 'bilinear' | 'hamming' | 'bicubic' | 'lanczos';
|
||||||
|
};
|
||||||
|
|
@ -23,6 +23,8 @@ import type { ImageLerpInvocation } from '../models/ImageLerpInvocation';
|
|||||||
import type { ImageMultiplyInvocation } from '../models/ImageMultiplyInvocation';
|
import type { ImageMultiplyInvocation } from '../models/ImageMultiplyInvocation';
|
||||||
import type { ImagePasteInvocation } from '../models/ImagePasteInvocation';
|
import type { ImagePasteInvocation } from '../models/ImagePasteInvocation';
|
||||||
import type { ImageProcessorInvocation } from '../models/ImageProcessorInvocation';
|
import type { ImageProcessorInvocation } from '../models/ImageProcessorInvocation';
|
||||||
|
import type { ImageResizeInvocation } from '../models/ImageResizeInvocation';
|
||||||
|
import type { ImageScaleInvocation } from '../models/ImageScaleInvocation';
|
||||||
import type { ImageToImageInvocation } from '../models/ImageToImageInvocation';
|
import type { ImageToImageInvocation } from '../models/ImageToImageInvocation';
|
||||||
import type { ImageToLatentsInvocation } from '../models/ImageToLatentsInvocation';
|
import type { ImageToLatentsInvocation } from '../models/ImageToLatentsInvocation';
|
||||||
import type { InfillColorInvocation } from '../models/InfillColorInvocation';
|
import type { InfillColorInvocation } from '../models/InfillColorInvocation';
|
||||||
@ -169,7 +171,7 @@ export class SessionsService {
|
|||||||
* The id of the session
|
* The id of the session
|
||||||
*/
|
*/
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
requestBody: (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageprocessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation),
|
requestBody: (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageResizeInvocation | ImageScaleInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageprocessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation),
|
||||||
}): CancelablePromise<string> {
|
}): CancelablePromise<string> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -206,7 +208,7 @@ export class SessionsService {
|
|||||||
* The path to the node in the graph
|
* The path to the node in the graph
|
||||||
*/
|
*/
|
||||||
nodePath: string,
|
nodePath: string,
|
||||||
requestBody: (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageprocessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation),
|
requestBody: (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageResizeInvocation | ImageScaleInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageprocessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation),
|
||||||
}): CancelablePromise<GraphExecutionState> {
|
}): CancelablePromise<GraphExecutionState> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
|
@ -20,7 +20,7 @@ const invokeAIFilledTrack = defineStyle((_props) => ({
|
|||||||
|
|
||||||
const invokeAITrack = defineStyle((_props) => {
|
const invokeAITrack = defineStyle((_props) => {
|
||||||
return {
|
return {
|
||||||
bg: 'base.800',
|
bg: 'none',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6877,6 +6877,11 @@ z-schema@~5.0.2:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
commander "^10.0.0"
|
commander "^10.0.0"
|
||||||
|
|
||||||
|
zod@^3.21.4:
|
||||||
|
version "3.21.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
|
||||||
|
integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==
|
||||||
|
|
||||||
zustand@^4.3.1:
|
zustand@^4.3.1:
|
||||||
version "4.3.7"
|
version "4.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.7.tgz#501b1f0393a7f1d103332e45ab574be5747fedce"
|
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.7.tgz#501b1f0393a7f1d103332e45ab574be5747fedce"
|
||||||
|
Loading…
Reference in New Issue
Block a user