mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-01-20 02:18:07 -05:00
Compare commits
21 Commits
v3.6.0rc3
...
ryan/seaml
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b763b9e4c | ||
|
|
7f3be627c2 | ||
|
|
cb7e56a9a3 | ||
|
|
1a710a4c12 | ||
|
|
d8d266d3be | ||
|
|
4716632c23 | ||
|
|
3c4150d153 | ||
|
|
b71b14d582 | ||
|
|
73481d4aec | ||
|
|
2c049a3b94 | ||
|
|
367de44a8b | ||
|
|
f5f378d04b | ||
|
|
823edbfdef | ||
|
|
29bbb27289 | ||
|
|
a23502f7ff | ||
|
|
ce64dbefce | ||
|
|
b47afdc3b5 | ||
|
|
cde9c3090f | ||
|
|
6924b04d7c | ||
|
|
83fbd4bdf2 | ||
|
|
6460dcc7e0 |
@@ -1,5 +1,6 @@
|
||||
# Copyright (c) 2023 Kyle Schouviller (https://github.com/kyle0654)
|
||||
|
||||
import contextlib
|
||||
from contextlib import ExitStack
|
||||
from functools import singledispatchmethod
|
||||
from typing import List, Literal, Optional, Union
|
||||
@@ -716,10 +717,23 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
||||
**self.unet.unet.model_dump(),
|
||||
context=context,
|
||||
)
|
||||
|
||||
# Prepare seamless context, if configured.
|
||||
seamless_context = contextlib.nullcontext()
|
||||
seamless_config = self.unet.seamless
|
||||
if seamless_config is not None:
|
||||
seamless_context = set_seamless(
|
||||
model=unet_info.context.model,
|
||||
axes=seamless_config.axes,
|
||||
skipped_layers=seamless_config.skipped_layers,
|
||||
skip_second_resnet=seamless_config.skip_second_resnet,
|
||||
skip_conv2=seamless_config.skip_conv2,
|
||||
)
|
||||
|
||||
with (
|
||||
ExitStack() as exit_stack,
|
||||
ModelPatcher.apply_freeu(unet_info.context.model, self.unet.freeu_config),
|
||||
set_seamless(unet_info.context.model, self.unet.seamless_axes),
|
||||
seamless_context,
|
||||
unet_info as unet,
|
||||
# Apply the LoRA after unet has been moved to its target device for faster patching.
|
||||
ModelPatcher.apply_lora_unet(unet, _lora_loader()),
|
||||
@@ -826,7 +840,19 @@ class LatentsToImageInvocation(BaseInvocation, WithMetadata):
|
||||
context=context,
|
||||
)
|
||||
|
||||
with set_seamless(vae_info.context.model, self.vae.seamless_axes), vae_info as vae:
|
||||
# Prepare seamless context, if configured.
|
||||
seamless_context = contextlib.nullcontext()
|
||||
seamless_config = self.vae.seamless
|
||||
if seamless_config is not None:
|
||||
seamless_context = set_seamless(
|
||||
model=vae_info.context.model,
|
||||
axes=seamless_config.axes,
|
||||
skipped_layers=seamless_config.skipped_layers,
|
||||
skip_second_resnet=seamless_config.skip_second_resnet,
|
||||
skip_conv2=seamless_config.skip_conv2,
|
||||
)
|
||||
|
||||
with seamless_context, vae_info as vae:
|
||||
latents = latents.to(vae.device)
|
||||
if self.fp32:
|
||||
vae.to(dtype=torch.float32)
|
||||
|
||||
@@ -19,6 +19,13 @@ from .baseinvocation import (
|
||||
)
|
||||
|
||||
|
||||
class SeamlessSettings(BaseModel):
|
||||
axes: List[str] = Field(description="Axes('x' and 'y') to which apply seamless")
|
||||
skipped_layers: int = Field(description="How much down layers skip when applying seamless")
|
||||
skip_second_resnet: bool = Field(description="Skip or not second resnet in down blocks when applying seamless")
|
||||
skip_conv2: bool = Field(description="Skip or not conv2 in down blocks when applying seamless")
|
||||
|
||||
|
||||
class ModelInfo(BaseModel):
|
||||
model_name: str = Field(description="Info to load submodel")
|
||||
base_model: BaseModelType = Field(description="Base model")
|
||||
@@ -36,8 +43,8 @@ class UNetField(BaseModel):
|
||||
unet: ModelInfo = Field(description="Info to load unet submodel")
|
||||
scheduler: ModelInfo = Field(description="Info to load scheduler submodel")
|
||||
loras: List[LoraInfo] = Field(description="Loras to apply on model loading")
|
||||
seamless_axes: List[str] = Field(default_factory=list, description='Axes("x" and "y") to which apply seamless')
|
||||
freeu_config: Optional[FreeUConfig] = Field(default=None, description="FreeU configuration")
|
||||
seamless: Optional[SeamlessSettings] = Field(default=None, description="Seamless settings applied to model")
|
||||
|
||||
|
||||
class ClipField(BaseModel):
|
||||
@@ -50,7 +57,7 @@ class ClipField(BaseModel):
|
||||
class VaeField(BaseModel):
|
||||
# TODO: better naming?
|
||||
vae: ModelInfo = Field(description="Info to load vae submodel")
|
||||
seamless_axes: List[str] = Field(default_factory=list, description='Axes("x" and "y") to which apply seamless')
|
||||
seamless: Optional[SeamlessSettings] = Field(default=None, description="Seamless settings applied to model")
|
||||
|
||||
|
||||
@invocation_output("unet_output")
|
||||
@@ -451,6 +458,11 @@ class SeamlessModeInvocation(BaseInvocation):
|
||||
)
|
||||
seamless_y: bool = InputField(default=True, input=Input.Any, description="Specify whether Y axis is seamless")
|
||||
seamless_x: bool = InputField(default=True, input=Input.Any, description="Specify whether X axis is seamless")
|
||||
skipped_layers: int = InputField(default=0, input=Input.Any, description="How much model's down layers to skip")
|
||||
skip_second_resnet: bool = InputField(
|
||||
default=True, input=Input.Any, description="Skip or not second resnet in down layers"
|
||||
)
|
||||
skip_conv2: bool = InputField(default=True, input=Input.Any, description="Skip or not conv2 in down layers")
|
||||
|
||||
def invoke(self, context: InvocationContext) -> SeamlessModeOutput:
|
||||
# Conditionally append 'x' and 'y' based on seamless_x and seamless_y
|
||||
@@ -465,9 +477,19 @@ class SeamlessModeInvocation(BaseInvocation):
|
||||
seamless_axes_list.append("y")
|
||||
|
||||
if unet is not None:
|
||||
unet.seamless_axes = seamless_axes_list
|
||||
unet.seamless = SeamlessSettings(
|
||||
axes=seamless_axes_list,
|
||||
skipped_layers=self.skipped_layers,
|
||||
skip_second_resnet=self.skip_second_resnet,
|
||||
skip_conv2=self.skip_conv2,
|
||||
)
|
||||
if vae is not None:
|
||||
vae.seamless_axes = seamless_axes_list
|
||||
vae.seamless = SeamlessSettings(
|
||||
axes=seamless_axes_list,
|
||||
skipped_layers=self.skipped_layers,
|
||||
skip_second_resnet=self.skip_second_resnet,
|
||||
skip_conv2=self.skip_conv2,
|
||||
)
|
||||
|
||||
return SeamlessModeOutput(unet=unet, vae=vae)
|
||||
|
||||
|
||||
@@ -25,71 +25,55 @@ def _conv_forward_asymmetric(self, input, weight, bias):
|
||||
|
||||
|
||||
@contextmanager
|
||||
def set_seamless(model: Union[UNet2DConditionModel, AutoencoderKL], seamless_axes: List[str]):
|
||||
def set_seamless(
|
||||
model: Union[UNet2DConditionModel, AutoencoderKL],
|
||||
axes: List[str],
|
||||
skipped_layers: int,
|
||||
skip_second_resnet: bool,
|
||||
skip_conv2: bool,
|
||||
):
|
||||
try:
|
||||
to_restore = []
|
||||
|
||||
for m_name, m in model.named_modules():
|
||||
if isinstance(model, UNet2DConditionModel):
|
||||
if ".attentions." in m_name:
|
||||
if not isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)):
|
||||
continue
|
||||
|
||||
if isinstance(model, UNet2DConditionModel) and m_name.startswith("down_blocks.") and ".resnets." in m_name:
|
||||
# down_blocks.1.resnets.1.conv1
|
||||
_, block_num, _, resnet_num, submodule_name = m_name.split(".")
|
||||
block_num = int(block_num)
|
||||
resnet_num = int(resnet_num)
|
||||
|
||||
# if block_num >= seamless_down_blocks:
|
||||
if block_num >= len(model.down_blocks) - skipped_layers:
|
||||
continue
|
||||
|
||||
if ".resnets." in m_name:
|
||||
if ".conv2" in m_name:
|
||||
continue
|
||||
if ".conv_shortcut" in m_name:
|
||||
continue
|
||||
|
||||
"""
|
||||
if isinstance(model, UNet2DConditionModel):
|
||||
if False and ".upsamplers." in m_name:
|
||||
if resnet_num > 0 and skip_second_resnet:
|
||||
continue
|
||||
|
||||
if False and ".downsamplers." in m_name:
|
||||
if submodule_name == "conv2" and skip_conv2:
|
||||
continue
|
||||
|
||||
if True and ".resnets." in m_name:
|
||||
if True and ".conv1" in m_name:
|
||||
if False and "down_blocks" in m_name:
|
||||
continue
|
||||
if False and "mid_block" in m_name:
|
||||
continue
|
||||
if False and "up_blocks" in m_name:
|
||||
continue
|
||||
m.asymmetric_padding_mode = {}
|
||||
m.asymmetric_padding = {}
|
||||
m.asymmetric_padding_mode["x"] = "circular" if ("x" in axes) else "constant"
|
||||
m.asymmetric_padding["x"] = (
|
||||
m._reversed_padding_repeated_twice[0],
|
||||
m._reversed_padding_repeated_twice[1],
|
||||
0,
|
||||
0,
|
||||
)
|
||||
m.asymmetric_padding_mode["y"] = "circular" if ("y" in axes) else "constant"
|
||||
m.asymmetric_padding["y"] = (
|
||||
0,
|
||||
0,
|
||||
m._reversed_padding_repeated_twice[2],
|
||||
m._reversed_padding_repeated_twice[3],
|
||||
)
|
||||
|
||||
if True and ".conv2" in m_name:
|
||||
continue
|
||||
|
||||
if True and ".conv_shortcut" in m_name:
|
||||
continue
|
||||
|
||||
if True and ".attentions." in m_name:
|
||||
continue
|
||||
|
||||
if False and m_name in ["conv_in", "conv_out"]:
|
||||
continue
|
||||
"""
|
||||
|
||||
if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)):
|
||||
m.asymmetric_padding_mode = {}
|
||||
m.asymmetric_padding = {}
|
||||
m.asymmetric_padding_mode["x"] = "circular" if ("x" in seamless_axes) else "constant"
|
||||
m.asymmetric_padding["x"] = (
|
||||
m._reversed_padding_repeated_twice[0],
|
||||
m._reversed_padding_repeated_twice[1],
|
||||
0,
|
||||
0,
|
||||
)
|
||||
m.asymmetric_padding_mode["y"] = "circular" if ("y" in seamless_axes) else "constant"
|
||||
m.asymmetric_padding["y"] = (
|
||||
0,
|
||||
0,
|
||||
m._reversed_padding_repeated_twice[2],
|
||||
m._reversed_padding_repeated_twice[3],
|
||||
)
|
||||
|
||||
to_restore.append((m, m._conv_forward))
|
||||
m._conv_forward = _conv_forward_asymmetric.__get__(m, nn.Conv2d)
|
||||
to_restore.append((m, m._conv_forward))
|
||||
m._conv_forward = _conv_forward_asymmetric.__get__(m, nn.Conv2d)
|
||||
|
||||
yield
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ def torch_dtype(device: torch.device) -> torch.dtype:
|
||||
if config.full_precision:
|
||||
return torch.float32
|
||||
if choose_precision(device) == "float16":
|
||||
return torch.float16
|
||||
return torch.bfloat16 if device.type == "cuda" else torch.float16
|
||||
else:
|
||||
return torch.float32
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
"@fontsource-variable/inter": "^5.0.16",
|
||||
"@mantine/form": "6.0.21",
|
||||
"@nanostores/react": "^0.7.1",
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"@reduxjs/toolkit": "2.0.1",
|
||||
"@roarr/browser-log-writer": "^1.3.0",
|
||||
"chakra-react-select": "^4.7.6",
|
||||
"compare-versions": "^6.1.0",
|
||||
@@ -94,7 +94,7 @@
|
||||
"react-i18next": "^14.0.0",
|
||||
"react-icons": "^4.12.0",
|
||||
"react-konva": "^18.2.10",
|
||||
"react-redux": "^9.0.4",
|
||||
"react-redux": "9.0.4",
|
||||
"react-resizable-panels": "^1.0.7",
|
||||
"react-select": "5.8.0",
|
||||
"react-textarea-autosize": "^8.5.3",
|
||||
@@ -167,5 +167,10 @@
|
||||
"vite-plugin-dts": "^3.7.0",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-tsconfig-paths": "^4.2.3"
|
||||
},
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"reselect@5.0.1": "patches/reselect@5.0.1.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
241
invokeai/frontend/web/patches/reselect@5.0.1.patch
Normal file
241
invokeai/frontend/web/patches/reselect@5.0.1.patch
Normal file
File diff suppressed because one or more lines are too long
14
invokeai/frontend/web/pnpm-lock.yaml
generated
14
invokeai/frontend/web/pnpm-lock.yaml
generated
@@ -4,6 +4,11 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
patchedDependencies:
|
||||
reselect@5.0.1:
|
||||
hash: kvbgwzjyy4x4fnh7znyocvb75q
|
||||
path: patches/reselect@5.0.1.patch
|
||||
|
||||
dependencies:
|
||||
'@chakra-ui/anatomy':
|
||||
specifier: ^2.2.2
|
||||
@@ -54,7 +59,7 @@ dependencies:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1(nanostores@0.9.5)(react@18.2.0)
|
||||
'@reduxjs/toolkit':
|
||||
specifier: ^2.0.1
|
||||
specifier: 2.0.1
|
||||
version: 2.0.1(react-redux@9.0.4)(react@18.2.0)
|
||||
'@roarr/browser-log-writer':
|
||||
specifier: ^1.3.0
|
||||
@@ -132,7 +137,7 @@ dependencies:
|
||||
specifier: ^18.2.10
|
||||
version: 18.2.10(konva@9.3.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
react-redux:
|
||||
specifier: ^9.0.4
|
||||
specifier: 9.0.4
|
||||
version: 9.0.4(@types/react@18.2.46)(react@18.2.0)(redux@5.0.1)
|
||||
react-resizable-panels:
|
||||
specifier: ^1.0.7
|
||||
@@ -4565,7 +4570,7 @@ packages:
|
||||
react-redux: 9.0.4(@types/react@18.2.46)(react@18.2.0)(redux@5.0.1)
|
||||
redux: 5.0.1
|
||||
redux-thunk: 3.1.0(redux@5.0.1)
|
||||
reselect: 5.0.1
|
||||
reselect: 5.0.1(patch_hash=kvbgwzjyy4x4fnh7znyocvb75q)
|
||||
dev: false
|
||||
|
||||
/@roarr/browser-log-writer@1.3.0:
|
||||
@@ -11932,9 +11937,10 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/reselect@5.0.1:
|
||||
/reselect@5.0.1(patch_hash=kvbgwzjyy4x4fnh7znyocvb75q):
|
||||
resolution: {integrity: sha512-D72j2ubjgHpvuCiORWkOUxndHJrxDaSolheiz5CO+roz8ka97/4msh2E8F5qay4GawR5vzBt5MkbDHT+Rdy/Wg==}
|
||||
dev: false
|
||||
patched: true
|
||||
|
||||
/resize-observer-polyfill@1.5.1:
|
||||
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { memo, useCallback, useEffect } from 'react';
|
||||
*/
|
||||
const Toaster = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const toastQueue = useAppSelector((state) => state.system.toastQueue);
|
||||
const toastQueue = useAppSelector((s) => s.system.toastQueue);
|
||||
const toast = useToast();
|
||||
useEffect(() => {
|
||||
toastQueue.forEach((t) => {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { createLogWriter } from '@roarr/browser-log-writer';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { ROARR, Roarr } from 'roarr';
|
||||
@@ -8,17 +6,9 @@ import { ROARR, Roarr } from 'roarr';
|
||||
import type { LoggerNamespace } from './logger';
|
||||
import { $logger, BASE_CONTEXT, LOG_LEVEL_MAP, logger } from './logger';
|
||||
|
||||
const selector = createMemoizedSelector(stateSelector, ({ system }) => {
|
||||
const { consoleLogLevel, shouldLogToConsole } = system;
|
||||
|
||||
return {
|
||||
consoleLogLevel,
|
||||
shouldLogToConsole,
|
||||
};
|
||||
});
|
||||
|
||||
export const useLogger = (namespace: LoggerNamespace) => {
|
||||
const { consoleLogLevel, shouldLogToConsole } = useAppSelector(selector);
|
||||
const consoleLogLevel = useAppSelector((s) => s.system.consoleLogLevel);
|
||||
const shouldLogToConsole = useAppSelector((s) => s.system.shouldLogToConsole);
|
||||
|
||||
// The provided Roarr browser log writer uses localStorage to config logging to console
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { createSelectorCreator, lruMemoize } from '@reduxjs/toolkit';
|
||||
import {
|
||||
createDraftSafeSelectorCreator,
|
||||
createSelectorCreator,
|
||||
lruMemoize,
|
||||
} from '@reduxjs/toolkit';
|
||||
import type { GetSelectorsOptions } from '@reduxjs/toolkit/dist/entities/state_selectors';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
/**
|
||||
@@ -19,3 +24,12 @@ export const createLruSelector = createSelectorCreator({
|
||||
memoize: lruMemoize,
|
||||
argsMemoize: lruMemoize,
|
||||
});
|
||||
|
||||
export const createLruDraftSafeSelector = createDraftSafeSelectorCreator({
|
||||
memoize: lruMemoize,
|
||||
argsMemoize: lruMemoize,
|
||||
});
|
||||
|
||||
export const getSelectorsOptions: GetSelectorsOptions = {
|
||||
createSelector: createLruDraftSafeSelector,
|
||||
};
|
||||
|
||||
@@ -8,7 +8,6 @@ import { initialPostprocessingState } from 'features/parameters/store/postproces
|
||||
import { initialSDXLState } from 'features/sdxl/store/sdxlSlice';
|
||||
import { initialConfigState } from 'features/system/store/configSlice';
|
||||
import { initialSystemState } from 'features/system/store/systemSlice';
|
||||
import { initialHotkeysState } from 'features/ui/store/hotkeysSlice';
|
||||
import { initialUIState } from 'features/ui/store/uiSlice';
|
||||
import { defaultsDeep } from 'lodash-es';
|
||||
import type { UnserializeFunction } from 'redux-remember';
|
||||
@@ -24,7 +23,6 @@ const initialStates: {
|
||||
system: initialSystemState,
|
||||
config: initialConfigState,
|
||||
ui: initialUIState,
|
||||
hotkeys: initialHotkeysState,
|
||||
controlAdapters: initialControlAdapterState,
|
||||
dynamicPrompts: initialDynamicPromptsState,
|
||||
sdxl: initialSDXLState,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||
import { IMAGE_CATEGORIES } from 'features/gallery/store/types';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import type { ImageCache } from 'services/api/types';
|
||||
import { getListImagesUrl, imagesAdapter } from 'services/api/util';
|
||||
import { getListImagesUrl, imagesSelectors } from 'services/api/util';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
@@ -33,7 +33,7 @@ export const addFirstListImagesListener = () => {
|
||||
|
||||
if (data.ids.length > 0) {
|
||||
// Select the first image
|
||||
const firstImage = imagesAdapter.getSelectors().selectAll(data)[0];
|
||||
const firstImage = imagesSelectors.selectAll(data)[0];
|
||||
dispatch(imageSelected(firstImage ?? null));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -20,9 +20,15 @@ export const addDeleteBoardAndImagesFulfilledListener = () => {
|
||||
let wasNodeEditorReset = false;
|
||||
let wereControlAdaptersReset = false;
|
||||
|
||||
const state = getState();
|
||||
const { generation, canvas, nodes, controlAdapters } = getState();
|
||||
deleted_images.forEach((image_name) => {
|
||||
const imageUsage = getImageUsage(state, image_name);
|
||||
const imageUsage = getImageUsage(
|
||||
generation,
|
||||
canvas,
|
||||
nodes,
|
||||
controlAdapters,
|
||||
image_name
|
||||
);
|
||||
|
||||
if (imageUsage.isInitialImage && !wasInitialImageReset) {
|
||||
dispatch(clearInitialImage());
|
||||
|
||||
@@ -17,7 +17,7 @@ import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
||||
import { clamp, forEach } from 'lodash-es';
|
||||
import { api } from 'services/api';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { imagesAdapter } from 'services/api/util';
|
||||
import { imagesSelectors } from 'services/api/util';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
@@ -53,9 +53,7 @@ export const addRequestedSingleImageDeletionListener = () => {
|
||||
const { data } =
|
||||
imagesApi.endpoints.listImages.select(baseQueryArgs)(state);
|
||||
|
||||
const cachedImageDTOs = data
|
||||
? imagesAdapter.getSelectors().selectAll(data)
|
||||
: [];
|
||||
const cachedImageDTOs = data ? imagesSelectors.selectAll(data) : [];
|
||||
|
||||
const deletedImageIndex = cachedImageDTOs.findIndex(
|
||||
(i) => i.image_name === image_name
|
||||
@@ -187,7 +185,7 @@ export const addRequestedMultipleImageDeletionListener = () => {
|
||||
imagesApi.endpoints.listImages.select(baseQueryArgs)(state);
|
||||
|
||||
const newSelectedImageDTO = data
|
||||
? imagesAdapter.getSelectors().selectAll(data)[0]
|
||||
? imagesSelectors.selectAll(data)[0]
|
||||
: undefined;
|
||||
|
||||
if (newSelectedImageDTO) {
|
||||
|
||||
@@ -17,9 +17,9 @@ import {
|
||||
import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice';
|
||||
import { forEach, some } from 'lodash-es';
|
||||
import {
|
||||
mainModelsAdapter,
|
||||
mainModelsAdapterSelectors,
|
||||
modelsApi,
|
||||
vaeModelsAdapter,
|
||||
vaeModelsAdapterSelectors,
|
||||
} from 'services/api/endpoints/models';
|
||||
import type { TypeGuardFor } from 'services/api/types';
|
||||
|
||||
@@ -43,7 +43,7 @@ export const addModelsLoadedListener = () => {
|
||||
);
|
||||
|
||||
const currentModel = getState().generation.model;
|
||||
const models = mainModelsAdapter.getSelectors().selectAll(action.payload);
|
||||
const models = mainModelsAdapterSelectors.selectAll(action.payload);
|
||||
|
||||
if (models.length === 0) {
|
||||
// No models loaded at all
|
||||
@@ -94,7 +94,7 @@ export const addModelsLoadedListener = () => {
|
||||
);
|
||||
|
||||
const currentModel = getState().sdxl.refinerModel;
|
||||
const models = mainModelsAdapter.getSelectors().selectAll(action.payload);
|
||||
const models = mainModelsAdapterSelectors.selectAll(action.payload);
|
||||
|
||||
if (models.length === 0) {
|
||||
// No models loaded at all
|
||||
@@ -145,9 +145,7 @@ export const addModelsLoadedListener = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstModel = vaeModelsAdapter
|
||||
.getSelectors()
|
||||
.selectAll(action.payload)[0];
|
||||
const firstModel = vaeModelsAdapterSelectors.selectAll(action.payload)[0];
|
||||
|
||||
if (!firstModel) {
|
||||
// No custom VAEs loaded at all; use the default
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { modelChanged } from 'features/parameters/store/generationSlice';
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import { NON_REFINER_BASE_MODELS } from 'services/api/constants';
|
||||
import { mainModelsAdapter, modelsApi } from 'services/api/endpoints/models';
|
||||
import {
|
||||
mainModelsAdapterSelectors,
|
||||
modelsApi,
|
||||
} from 'services/api/endpoints/models';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
@@ -37,8 +40,7 @@ export const addTabChangedListener = () => {
|
||||
}
|
||||
|
||||
// need to filter out all the invalid canvas models (currently refiner & any)
|
||||
const validCanvasModels = mainModelsAdapter
|
||||
.getSelectors()
|
||||
const validCanvasModels = mainModelsAdapterSelectors
|
||||
.selectAll(models)
|
||||
.filter((model) =>
|
||||
['sd-1', 'sd-2', 'sdxl'].includes(model.base_model)
|
||||
|
||||
@@ -151,6 +151,3 @@ export type RootState = ReturnType<ReturnType<typeof createStore>['getState']>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type AppThunkDispatch = ThunkDispatch<RootState, any, UnknownAction>;
|
||||
export type AppDispatch = ReturnType<typeof createStore>['dispatch'];
|
||||
export function stateSelector(state: RootState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ const IAIInformationalPopover = ({
|
||||
...rest
|
||||
}: Props) => {
|
||||
const shouldEnableInformationalPopovers = useAppSelector(
|
||||
(state) => state.system.shouldEnableInformationalPopovers
|
||||
(s) => s.system.shouldEnableInformationalPopovers
|
||||
);
|
||||
|
||||
const data = useMemo(() => POPOVER_DATA[feature], [feature]);
|
||||
|
||||
@@ -21,7 +21,7 @@ export const InvAccordionButton = (props: InvAccordionButtonProps) => {
|
||||
{children}
|
||||
<Spacer />
|
||||
{badges?.map((b, i) => (
|
||||
<InvBadge key={`${b}.${i}`} colorScheme="blue">
|
||||
<InvBadge key={`${b}.${i}`} colorScheme="invokeBlue">
|
||||
{b}
|
||||
</InvBadge>
|
||||
))}
|
||||
|
||||
@@ -28,7 +28,13 @@ const meta: Meta<typeof InvButton> = {
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof InvButton>;
|
||||
|
||||
const colorSchemes = ['base', 'invokeYellow', 'red', 'green', 'blue'] as const;
|
||||
const colorSchemes = [
|
||||
'base',
|
||||
'invokeYellow',
|
||||
'invokeRed',
|
||||
'invokeGreen',
|
||||
'invokeBlue',
|
||||
] as const;
|
||||
const variants = ['solid', 'outline', 'ghost', 'link'] as const;
|
||||
const sizes = ['xs', 'sm', 'md', 'lg'] as const;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export const InvButton = memo(
|
||||
<InvTooltip label={tooltip}>
|
||||
<Button
|
||||
ref={ref}
|
||||
colorScheme={isChecked ? 'blue' : 'base'}
|
||||
colorScheme={isChecked ? 'invokeBlue' : 'base'}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
@@ -22,7 +22,11 @@ export const InvButton = memo(
|
||||
}
|
||||
|
||||
return (
|
||||
<Button ref={ref} colorScheme={isChecked ? 'blue' : 'base'} {...rest}>
|
||||
<Button
|
||||
ref={ref}
|
||||
colorScheme={isChecked ? 'invokeBlue' : 'base'}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ export const InvLabel = memo(
|
||||
ref
|
||||
) => {
|
||||
const shouldEnableInformationalPopovers = useAppSelector(
|
||||
(state) => state.system.shouldEnableInformationalPopovers
|
||||
(s) => s.system.shouldEnableInformationalPopovers
|
||||
);
|
||||
|
||||
const ctx = useContext(InvControlGroupContext);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { defineStyle, defineStyleConfig } from '@chakra-ui/react';
|
||||
|
||||
const blue = defineStyle(() => ({
|
||||
color: 'blue.300',
|
||||
const invokeBlue = defineStyle(() => ({
|
||||
color: 'invokeBlue.300',
|
||||
}));
|
||||
|
||||
export const headingTheme = defineStyleConfig({
|
||||
variants: {
|
||||
blue,
|
||||
invokeBlue,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ export const InvIconButton = memo(
|
||||
<InvTooltip label={tooltip}>
|
||||
<IconButton
|
||||
ref={ref}
|
||||
colorScheme={isChecked ? 'blue' : 'base'}
|
||||
colorScheme={isChecked ? 'invokeBlue' : 'base'}
|
||||
{...rest}
|
||||
/>
|
||||
</InvTooltip>
|
||||
@@ -21,7 +21,7 @@ export const InvIconButton = memo(
|
||||
return (
|
||||
<IconButton
|
||||
ref={ref}
|
||||
colorScheme={isChecked ? 'blue' : 'base'}
|
||||
colorScheme={isChecked ? 'invokeBlue' : 'base'}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -30,7 +30,7 @@ export const useGroupedModelInvSelect = <T extends AnyModelConfigEntity>(
|
||||
): UseGroupedModelInvSelectReturn => {
|
||||
const { t } = useTranslation();
|
||||
const base_model = useAppSelector(
|
||||
(state) => state.generation.model?.base_model ?? 'sdxl'
|
||||
(s) => s.generation.model?.base_model ?? 'sdxl'
|
||||
);
|
||||
const { modelEntities, selectedModel, getIsDisabled, onChange, isLoading } =
|
||||
arg;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useAppToaster } from 'app/components/Toaster';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
@@ -15,9 +14,9 @@ const accept: Accept = {
|
||||
'image/jpeg': ['.jpg', '.jpeg', '.png'],
|
||||
};
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[stateSelector, activeTabNameSelector],
|
||||
({ gallery }, activeTabName) => {
|
||||
const selectPostUploadAction = createMemoizedSelector(
|
||||
activeTabNameSelector,
|
||||
(activeTabName) => {
|
||||
let postUploadAction: PostUploadAction = { type: 'TOAST' };
|
||||
|
||||
if (activeTabName === 'unifiedCanvas') {
|
||||
@@ -28,19 +27,15 @@ const selector = createMemoizedSelector(
|
||||
postUploadAction = { type: 'SET_INITIAL_IMAGE' };
|
||||
}
|
||||
|
||||
const { autoAddBoardId } = gallery;
|
||||
|
||||
return {
|
||||
autoAddBoardId,
|
||||
postUploadAction,
|
||||
};
|
||||
return postUploadAction;
|
||||
}
|
||||
);
|
||||
|
||||
export const useFullscreenDropzone = () => {
|
||||
const { autoAddBoardId, postUploadAction } = useAppSelector(selector);
|
||||
const toaster = useAppToaster();
|
||||
const { t } = useTranslation();
|
||||
const toaster = useAppToaster();
|
||||
const postUploadAction = useAppSelector(selectPostUploadAction);
|
||||
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
|
||||
const [isHandlingUpload, setIsHandlingUpload] = useState<boolean>(false);
|
||||
|
||||
const [uploadImage] = useUploadImageMutation();
|
||||
|
||||
@@ -32,9 +32,7 @@ export const useImageUploadButton = ({
|
||||
postUploadAction,
|
||||
isDisabled,
|
||||
}: UseImageUploadButtonArgs) => {
|
||||
const autoAddBoardId = useAppSelector(
|
||||
(state) => state.gallery.autoAddBoardId
|
||||
);
|
||||
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
|
||||
const [uploadImage] = useUploadImageMutation();
|
||||
const onDropAccepted = useCallback(
|
||||
(files: File[]) => {
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterAll } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterAll,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
||||
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import { selectNodeTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice';
|
||||
import { isInvocationNode } from 'features/nodes/types/invocation';
|
||||
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
||||
import { selectSystemSlice } from 'features/system/store/systemSlice';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import i18n from 'i18next';
|
||||
import { forEach } from 'lodash-es';
|
||||
import { getConnectedEdges } from 'reactflow';
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[stateSelector, activeTabNameSelector],
|
||||
[
|
||||
selectControlAdaptersSlice,
|
||||
selectGenerationSlice,
|
||||
selectSystemSlice,
|
||||
selectNodesSlice,
|
||||
selectNodeTemplatesSlice,
|
||||
selectDynamicPromptsSlice,
|
||||
activeTabNameSelector,
|
||||
],
|
||||
(
|
||||
{
|
||||
controlAdapters,
|
||||
generation,
|
||||
system,
|
||||
nodes,
|
||||
nodeTemplates,
|
||||
dynamicPrompts,
|
||||
},
|
||||
controlAdapters,
|
||||
generation,
|
||||
system,
|
||||
nodes,
|
||||
nodeTemplates,
|
||||
dynamicPrompts,
|
||||
activeTabName
|
||||
) => {
|
||||
const { initialImage, model, positivePrompt } = generation;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Box, chakra, Flex } from '@chakra-ui/react';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import useCanvasDragMove from 'features/canvas/hooks/useCanvasDragMove';
|
||||
import useCanvasHotkeys from 'features/canvas/hooks/useCanvasHotkeys';
|
||||
@@ -17,7 +16,10 @@ import {
|
||||
$isTransformingBoundingBox,
|
||||
} from 'features/canvas/store/canvasNanostore';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { canvasResized } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
canvasResized,
|
||||
selectCanvasSlice,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
setCanvasBaseLayer,
|
||||
setCanvasStage,
|
||||
@@ -40,57 +42,34 @@ import IAICanvasStatusText from './IAICanvasStatusText';
|
||||
import IAICanvasBoundingBox from './IAICanvasToolbar/IAICanvasBoundingBox';
|
||||
import IAICanvasToolPreview from './IAICanvasToolPreview';
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[stateSelector, isStagingSelector],
|
||||
({ canvas }, isStaging) => {
|
||||
const {
|
||||
isMaskEnabled,
|
||||
stageScale,
|
||||
shouldShowBoundingBox,
|
||||
stageDimensions,
|
||||
stageCoordinates,
|
||||
tool,
|
||||
shouldShowIntermediates,
|
||||
shouldRestrictStrokesToBox,
|
||||
shouldShowGrid,
|
||||
shouldAntialias,
|
||||
} = canvas;
|
||||
|
||||
return {
|
||||
isMaskEnabled,
|
||||
shouldShowBoundingBox,
|
||||
shouldShowGrid,
|
||||
stageCoordinates,
|
||||
stageDimensions,
|
||||
stageScale,
|
||||
tool,
|
||||
isStaging,
|
||||
shouldShowIntermediates,
|
||||
shouldAntialias,
|
||||
shouldRestrictStrokesToBox,
|
||||
};
|
||||
}
|
||||
);
|
||||
const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
return {
|
||||
stageCoordinates: canvas.stageCoordinates,
|
||||
stageDimensions: canvas.stageDimensions,
|
||||
};
|
||||
});
|
||||
|
||||
const ChakraStage = chakra(Stage, {
|
||||
shouldForwardProp: (prop) => !['sx'].includes(prop),
|
||||
});
|
||||
|
||||
const IAICanvas = () => {
|
||||
const {
|
||||
isMaskEnabled,
|
||||
shouldShowBoundingBox,
|
||||
shouldShowGrid,
|
||||
stageCoordinates,
|
||||
stageDimensions,
|
||||
stageScale,
|
||||
tool,
|
||||
isStaging,
|
||||
shouldShowIntermediates,
|
||||
shouldAntialias,
|
||||
shouldRestrictStrokesToBox,
|
||||
} = useAppSelector(selector);
|
||||
useCanvasHotkeys();
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
|
||||
const shouldShowBoundingBox = useAppSelector(
|
||||
(s) => s.canvas.shouldShowBoundingBox
|
||||
);
|
||||
const shouldShowGrid = useAppSelector((s) => s.canvas.shouldShowGrid);
|
||||
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||
const tool = useAppSelector((s) => s.canvas.tool);
|
||||
const shouldShowIntermediates = useAppSelector(
|
||||
(s) => s.canvas.shouldShowIntermediates
|
||||
);
|
||||
const shouldAntialias = useAppSelector((s) => s.canvas.shouldAntialias);
|
||||
const shouldRestrictStrokesToBox = useAppSelector(
|
||||
(s) => s.canvas.shouldRestrictStrokesToBox
|
||||
);
|
||||
const { stageCoordinates, stageDimensions } = useAppSelector(selector);
|
||||
const dispatch = useAppDispatch();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const stageRef = useRef<Konva.Stage | null>(null);
|
||||
@@ -99,6 +78,7 @@ const IAICanvas = () => {
|
||||
const isMovingStage = useStore($isMovingStage);
|
||||
const isTransformingBoundingBox = useStore($isTransformingBoundingBox);
|
||||
const isMouseOverBoundingBox = useStore($isMouseOverBoundingBox);
|
||||
useCanvasHotkeys();
|
||||
const canvasStageRefCallback = useCallback((el: Konva.Stage) => {
|
||||
setCanvasStage(el as Konva.Stage);
|
||||
stageRef.current = el;
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
import { memo } from 'react';
|
||||
import { Group, Rect } from 'react-konva';
|
||||
|
||||
const selector = createMemoizedSelector(stateSelector, ({ canvas }) => {
|
||||
const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
const {
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
stageDimensions,
|
||||
stageScale,
|
||||
shouldDarkenOutsideBoundingBox,
|
||||
stageCoordinates,
|
||||
} = canvas;
|
||||
|
||||
return {
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
shouldDarkenOutsideBoundingBox,
|
||||
stageCoordinates,
|
||||
stageDimensions,
|
||||
stageScale,
|
||||
};
|
||||
});
|
||||
|
||||
const IAICanvasBoundingBoxOverlay = () => {
|
||||
const {
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
shouldDarkenOutsideBoundingBox,
|
||||
stageCoordinates,
|
||||
stageDimensions,
|
||||
stageScale,
|
||||
} = useAppSelector(selector);
|
||||
const shouldDarkenOutsideBoundingBox = useAppSelector(
|
||||
(s) => s.canvas.shouldDarkenOutsideBoundingBox
|
||||
);
|
||||
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||
|
||||
return (
|
||||
<Group listening={false}>
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
// Grid drawing adapted from https://longviewcoder.com/2021/12/08/konva-a-better-grid/
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
import type { ReactElement } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { Group, Line as KonvaLine } from 'react-konva';
|
||||
import { getArbitraryBaseColor } from 'theme/colors';
|
||||
|
||||
const selector = createMemoizedSelector([stateSelector], ({ canvas }) => {
|
||||
const { stageScale, stageCoordinates, stageDimensions } = canvas;
|
||||
return { stageScale, stageCoordinates, stageDimensions };
|
||||
const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
return {
|
||||
stageCoordinates: canvas.stageCoordinates,
|
||||
stageDimensions: canvas.stageDimensions,
|
||||
};
|
||||
});
|
||||
|
||||
const baseGridLineColor = getArbitraryBaseColor(27);
|
||||
const fineGridLineColor = getArbitraryBaseColor(18);
|
||||
|
||||
const IAICanvasGrid = () => {
|
||||
const { stageScale, stageCoordinates, stageDimensions } =
|
||||
useAppSelector(selector);
|
||||
const { stageCoordinates, stageDimensions } = useAppSelector(selector);
|
||||
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||
|
||||
const gridSpacing = useMemo(() => {
|
||||
if (stageScale >= 2) {
|
||||
|
||||
@@ -1,32 +1,28 @@
|
||||
import {
|
||||
createLruSelector,
|
||||
createMemoizedSelector,
|
||||
} from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
import { selectSystemSlice } from 'features/system/store/systemSlice';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { Image as KonvaImage } from 'react-konva';
|
||||
|
||||
const progressImageSelector = createLruSelector(
|
||||
[stateSelector],
|
||||
({ system, canvas }) => {
|
||||
const progressImageSelector = createMemoizedSelector(
|
||||
[selectSystemSlice, selectCanvasSlice],
|
||||
(system, canvas) => {
|
||||
const { denoiseProgress } = system;
|
||||
const { batchIds } = canvas;
|
||||
|
||||
return denoiseProgress && batchIds.includes(denoiseProgress.batch_id)
|
||||
? denoiseProgress.progress_image
|
||||
: undefined;
|
||||
return {
|
||||
progressImage:
|
||||
denoiseProgress && batchIds.includes(denoiseProgress.batch_id)
|
||||
? denoiseProgress.progress_image
|
||||
: undefined,
|
||||
boundingBox: canvas.layerState.stagingArea.boundingBox,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const boundingBoxSelector = createMemoizedSelector(
|
||||
[stateSelector],
|
||||
({ canvas }) => canvas.layerState.stagingArea.boundingBox
|
||||
);
|
||||
|
||||
const IAICanvasIntermediateImage = () => {
|
||||
const progressImage = useAppSelector(progressImageSelector);
|
||||
const boundingBox = useAppSelector(boundingBoxSelector);
|
||||
const { progressImage, boundingBox } = useAppSelector(progressImageSelector);
|
||||
const [loadedImageElement, setLoadedImageElement] =
|
||||
useState<HTMLImageElement | null>(null);
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||
import { getColoredMaskSVG } from 'features/canvas/util/getColoredMaskSVG';
|
||||
import type Konva from 'konva';
|
||||
import type { RectConfig } from 'konva/lib/shapes/Rect';
|
||||
import { isNumber } from 'lodash-es';
|
||||
@@ -9,109 +10,27 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Rect } from 'react-konva';
|
||||
|
||||
export const canvasMaskCompositerSelector = createMemoizedSelector(
|
||||
stateSelector,
|
||||
({ canvas }) => {
|
||||
const { maskColor, stageCoordinates, stageDimensions, stageScale } = canvas;
|
||||
|
||||
selectCanvasSlice,
|
||||
(canvas) => {
|
||||
return {
|
||||
stageCoordinates,
|
||||
stageDimensions,
|
||||
stageScale,
|
||||
maskColorString: rgbaColorToString(maskColor),
|
||||
stageCoordinates: canvas.stageCoordinates,
|
||||
stageDimensions: canvas.stageDimensions,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
type IAICanvasMaskCompositerProps = RectConfig;
|
||||
|
||||
const getColoredSVG = (color: string) => {
|
||||
return `data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="60px" height="60px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||
<g transform="matrix(0.5,0,0,0.5,0,0)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,2.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,7.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,10)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,12.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,15)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,17.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,20)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,22.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,25)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,27.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,30)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-2.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-7.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-10)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-12.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-15)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-17.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-20)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-22.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-25)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-27.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-30)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
</svg>`.replaceAll('black', color);
|
||||
};
|
||||
|
||||
const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
|
||||
const { ...rest } = props;
|
||||
|
||||
const { maskColorString, stageCoordinates, stageDimensions, stageScale } =
|
||||
useAppSelector(canvasMaskCompositerSelector);
|
||||
|
||||
const { stageCoordinates, stageDimensions } = useAppSelector(
|
||||
canvasMaskCompositerSelector
|
||||
);
|
||||
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||
const maskColorString = useAppSelector((s) =>
|
||||
rgbaColorToString(s.canvas.maskColor)
|
||||
);
|
||||
const [fillPatternImage, setFillPatternImage] =
|
||||
useState<HTMLImageElement | null>(null);
|
||||
|
||||
@@ -132,14 +51,14 @@ const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
|
||||
image.onload = () => {
|
||||
setFillPatternImage(image);
|
||||
};
|
||||
image.src = getColoredSVG(maskColorString);
|
||||
image.src = getColoredMaskSVG(maskColorString);
|
||||
}, [fillPatternImage, maskColorString]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!fillPatternImage) {
|
||||
return;
|
||||
}
|
||||
fillPatternImage.src = getColoredSVG(maskColorString);
|
||||
fillPatternImage.src = getColoredMaskSVG(maskColorString);
|
||||
}, [fillPatternImage, maskColorString]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { isCanvasMaskLine } from 'features/canvas/store/canvasTypes';
|
||||
import type { GroupConfig } from 'konva/lib/Group';
|
||||
import { memo } from 'react';
|
||||
import { Group, Line } from 'react-konva';
|
||||
|
||||
export const canvasLinesSelector = createMemoizedSelector(
|
||||
[stateSelector],
|
||||
({ canvas }) => {
|
||||
return canvas.layerState.objects.filter(isCanvasMaskLine);
|
||||
}
|
||||
);
|
||||
|
||||
type InpaintingCanvasLinesProps = GroupConfig;
|
||||
|
||||
/**
|
||||
@@ -21,7 +12,7 @@ type InpaintingCanvasLinesProps = GroupConfig;
|
||||
* Uses globalCompositeOperation to handle the brush and eraser tools.
|
||||
*/
|
||||
const IAICanvasLines = (props: InpaintingCanvasLinesProps) => {
|
||||
const objects = useAppSelector((state) => state.canvas.layerState.objects);
|
||||
const objects = useAppSelector((s) => s.canvas.layerState.objects);
|
||||
|
||||
return (
|
||||
<Group listening={false} {...props}>
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Group, Line, Rect } from 'react-konva';
|
||||
import IAICanvasImage from './IAICanvasImage';
|
||||
|
||||
const IAICanvasObjectRenderer = () => {
|
||||
const objects = useAppSelector((state) => state.canvas.layerState.objects);
|
||||
const objects = useAppSelector((s) => s.canvas.layerState.objects);
|
||||
|
||||
return (
|
||||
<Group name="outpainting-objects" listening={false}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
import type { GroupConfig } from 'konva/lib/Group';
|
||||
import { memo } from 'react';
|
||||
import { Group, Rect } from 'react-konva';
|
||||
@@ -9,7 +9,7 @@ import IAICanvasImage from './IAICanvasImage';
|
||||
|
||||
const dash = [4, 4];
|
||||
|
||||
const selector = createMemoizedSelector([stateSelector], ({ canvas }) => {
|
||||
const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
const {
|
||||
layerState,
|
||||
shouldShowStagingImage,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||
import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup';
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
discardStagedImages,
|
||||
nextStagingAreaImage,
|
||||
prevStagingAreaImage,
|
||||
selectCanvasSlice,
|
||||
setShouldShowStagingImage,
|
||||
setShouldShowStagingOutline,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
} from 'react-icons/fa';
|
||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||
|
||||
const selector = createMemoizedSelector([stateSelector], ({ canvas }) => {
|
||||
const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
const {
|
||||
layerState: {
|
||||
stagingArea: { images, selectedImageIndex },
|
||||
@@ -142,7 +142,7 @@ const IAICanvasStagingAreaToolbar = () => {
|
||||
aria-label={`${t('unifiedCanvas.previous')} (Left)`}
|
||||
icon={<FaArrowLeft />}
|
||||
onClick={handlePrevImage}
|
||||
colorScheme="blue"
|
||||
colorScheme="invokeBlue"
|
||||
isDisabled={!shouldShowStagingImage}
|
||||
/>
|
||||
<InvButton
|
||||
@@ -156,7 +156,7 @@ const IAICanvasStagingAreaToolbar = () => {
|
||||
aria-label={`${t('unifiedCanvas.next')} (Right)`}
|
||||
icon={<FaArrowRight />}
|
||||
onClick={handleNextImage}
|
||||
colorScheme="blue"
|
||||
colorScheme="invokeBlue"
|
||||
isDisabled={!shouldShowStagingImage}
|
||||
/>
|
||||
</InvButtonGroup>
|
||||
@@ -166,7 +166,7 @@ const IAICanvasStagingAreaToolbar = () => {
|
||||
aria-label={`${t('unifiedCanvas.accept')} (Enter)`}
|
||||
icon={<FaCheck />}
|
||||
onClick={handleAccept}
|
||||
colorScheme="blue"
|
||||
colorScheme="invokeBlue"
|
||||
/>
|
||||
<InvIconButton
|
||||
tooltip={
|
||||
@@ -182,7 +182,7 @@ const IAICanvasStagingAreaToolbar = () => {
|
||||
data-alert={!shouldShowStagingImage}
|
||||
icon={shouldShowStagingImage ? <FaEye /> : <FaEyeSlash />}
|
||||
onClick={handleToggleShouldShowStagingImage}
|
||||
colorScheme="blue"
|
||||
colorScheme="invokeBlue"
|
||||
/>
|
||||
<InvIconButton
|
||||
tooltip={t('unifiedCanvas.saveToGallery')}
|
||||
@@ -190,7 +190,7 @@ const IAICanvasStagingAreaToolbar = () => {
|
||||
isDisabled={!imageDTO || !imageDTO.is_intermediate}
|
||||
icon={<FaSave />}
|
||||
onClick={handleSaveToGallery}
|
||||
colorScheme="blue"
|
||||
colorScheme="invokeBlue"
|
||||
/>
|
||||
<InvIconButton
|
||||
tooltip={t('unifiedCanvas.discardAll')}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
import roundToHundreth from 'features/canvas/util/roundToHundreth';
|
||||
import GenerationModeStatusText from 'features/parameters/components/Canvas/GenerationModeStatusText';
|
||||
import { memo } from 'react';
|
||||
@@ -11,7 +11,7 @@ import IAICanvasStatusTextCursorPos from './IAICanvasStatusText/IAICanvasStatusT
|
||||
|
||||
const warningColor = 'var(--invokeai-colors-warning-500)';
|
||||
|
||||
const selector = createMemoizedSelector([stateSelector], ({ canvas }) => {
|
||||
const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||
const {
|
||||
stageDimensions: { width: stageWidth, height: stageHeight },
|
||||
stageCoordinates: { x: stageX, y: stageY },
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
$cursorPosition,
|
||||
$isMovingBoundingBox,
|
||||
$isTransformingBoundingBox,
|
||||
} from 'features/canvas/store/canvasNanostore';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||
import {
|
||||
COLOR_PICKER_SIZE,
|
||||
@@ -17,16 +17,9 @@ import { memo, useMemo } from 'react';
|
||||
import { Circle, Group } from 'react-konva';
|
||||
|
||||
const canvasBrushPreviewSelector = createMemoizedSelector(
|
||||
stateSelector,
|
||||
({ canvas }) => {
|
||||
selectCanvasSlice,
|
||||
(canvas) => {
|
||||
const {
|
||||
brushSize,
|
||||
colorPickerColor,
|
||||
maskColor,
|
||||
brushColor,
|
||||
tool,
|
||||
layer,
|
||||
stageScale,
|
||||
stageDimensions,
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
@@ -82,17 +75,6 @@ const canvasBrushPreviewSelector = createMemoizedSelector(
|
||||
// : undefined;
|
||||
|
||||
return {
|
||||
radius: brushSize / 2,
|
||||
colorPickerOuterRadius: COLOR_PICKER_SIZE / stageScale,
|
||||
colorPickerInnerRadius:
|
||||
(COLOR_PICKER_SIZE - COLOR_PICKER_STROKE_RADIUS + 1) / stageScale,
|
||||
maskColorString: rgbaColorToString({ ...maskColor, a: 0.5 }),
|
||||
brushColorString: rgbaColorToString(brushColor),
|
||||
colorPickerColorString: rgbaColorToString(colorPickerColor),
|
||||
tool,
|
||||
layer,
|
||||
strokeWidth: 1.5 / stageScale,
|
||||
dotRadius: 1.5 / stageScale,
|
||||
clip,
|
||||
stageDimensions,
|
||||
};
|
||||
@@ -103,20 +85,28 @@ const canvasBrushPreviewSelector = createMemoizedSelector(
|
||||
* Draws a black circle around the canvas brush preview.
|
||||
*/
|
||||
const IAICanvasToolPreview = (props: GroupConfig) => {
|
||||
const {
|
||||
radius,
|
||||
maskColorString,
|
||||
tool,
|
||||
layer,
|
||||
dotRadius,
|
||||
strokeWidth,
|
||||
brushColorString,
|
||||
colorPickerColorString,
|
||||
colorPickerInnerRadius,
|
||||
colorPickerOuterRadius,
|
||||
clip,
|
||||
stageDimensions,
|
||||
} = useAppSelector(canvasBrushPreviewSelector);
|
||||
const radius = useAppSelector((s) => s.canvas.brushSize / 2);
|
||||
const maskColorString = useAppSelector((s) =>
|
||||
rgbaColorToString({ ...s.canvas.maskColor, a: 0.5 })
|
||||
);
|
||||
const tool = useAppSelector((s) => s.canvas.tool);
|
||||
const layer = useAppSelector((s) => s.canvas.layer);
|
||||
const dotRadius = useAppSelector((s) => 1.5 / s.canvas.stageScale);
|
||||
const strokeWidth = useAppSelector((s) => 1.5 / s.canvas.stageScale);
|
||||
const brushColorString = useAppSelector((s) =>
|
||||
rgbaColorToString(s.canvas.brushColor)
|
||||
);
|
||||
const colorPickerColorString = useAppSelector((s) =>
|
||||
rgbaColorToString(s.canvas.colorPickerColor)
|
||||
);
|
||||
const colorPickerInnerRadius = useAppSelector(
|
||||
(s) =>
|
||||
(COLOR_PICKER_SIZE - COLOR_PICKER_STROKE_RADIUS + 1) / s.canvas.stageScale
|
||||
);
|
||||
const colorPickerOuterRadius = useAppSelector(
|
||||
(s) => COLOR_PICKER_SIZE / s.canvas.stageScale
|
||||
);
|
||||
const { clip, stageDimensions } = useAppSelector(canvasBrushPreviewSelector);
|
||||
|
||||
const cursorPosition = useStore($cursorPosition);
|
||||
const isMovingBoundingBox = useStore($isMovingBoundingBox);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { $shift } from 'common/hooks/useGlobalModifiers';
|
||||
import {
|
||||
@@ -38,47 +36,23 @@ import { Group, Rect, Transformer } from 'react-konva';
|
||||
|
||||
const borderDash = [4, 4];
|
||||
|
||||
const boundingBoxPreviewSelector = createMemoizedSelector(
|
||||
[stateSelector, selectOptimalDimension],
|
||||
({ canvas }, optimalDimension) => {
|
||||
const {
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
stageScale,
|
||||
tool,
|
||||
shouldSnapToGrid,
|
||||
aspectRatio,
|
||||
} = canvas;
|
||||
|
||||
return {
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
stageScale,
|
||||
shouldSnapToGrid,
|
||||
tool,
|
||||
hitStrokeWidth: 20 / stageScale,
|
||||
aspectRatio,
|
||||
optimalDimension,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
type IAICanvasBoundingBoxPreviewProps = GroupConfig;
|
||||
|
||||
const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
||||
const { ...rest } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
stageScale,
|
||||
shouldSnapToGrid,
|
||||
tool,
|
||||
hitStrokeWidth,
|
||||
aspectRatio,
|
||||
optimalDimension,
|
||||
} = useAppSelector(boundingBoxPreviewSelector);
|
||||
|
||||
const boundingBoxCoordinates = useAppSelector(
|
||||
(s) => s.canvas.boundingBoxCoordinates
|
||||
);
|
||||
const boundingBoxDimensions = useAppSelector(
|
||||
(s) => s.canvas.boundingBoxDimensions
|
||||
);
|
||||
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||
const shouldSnapToGrid = useAppSelector((s) => s.canvas.shouldSnapToGrid);
|
||||
const tool = useAppSelector((s) => s.canvas.tool);
|
||||
const hitStrokeWidth = useAppSelector((s) => 20 / s.canvas.stageScale);
|
||||
const aspectRatio = useAppSelector((s) => s.canvas.aspectRatio);
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const transformerRef = useRef<Konva.Transformer>(null);
|
||||
const shapeRef = useRef<Konva.Rect>(null);
|
||||
const shift = useStore($shift);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||
@@ -22,7 +20,6 @@ import {
|
||||
setMaskColor,
|
||||
setShouldPreserveMaskedArea,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import type { RgbaColor } from 'react-colorful';
|
||||
@@ -30,33 +27,16 @@ import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaMask, FaSave, FaTrash } from 'react-icons/fa';
|
||||
|
||||
export const selector = createMemoizedSelector(
|
||||
[stateSelector, isStagingSelector],
|
||||
({ canvas }, isStaging) => {
|
||||
const { maskColor, layer, isMaskEnabled, shouldPreserveMaskedArea } =
|
||||
canvas;
|
||||
|
||||
return {
|
||||
layer,
|
||||
maskColor,
|
||||
maskColorString: rgbaColorToString(maskColor),
|
||||
isMaskEnabled,
|
||||
shouldPreserveMaskedArea,
|
||||
isStaging,
|
||||
};
|
||||
}
|
||||
);
|
||||
const IAICanvasMaskOptions = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
layer,
|
||||
maskColor,
|
||||
isMaskEnabled,
|
||||
shouldPreserveMaskedArea,
|
||||
isStaging,
|
||||
} = useAppSelector(selector);
|
||||
const layer = useAppSelector((s) => s.canvas.layer);
|
||||
const maskColor = useAppSelector((s) => s.canvas.maskColor);
|
||||
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
|
||||
const shouldPreserveMaskedArea = useAppSelector(
|
||||
(s) => s.canvas.shouldPreserveMaskedArea
|
||||
);
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
|
||||
useHotkeys(
|
||||
['q'],
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
||||
import { redo } from 'features/canvas/store/canvasSlice';
|
||||
@@ -9,21 +7,10 @@ import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaRedo } from 'react-icons/fa';
|
||||
|
||||
const canvasRedoSelector = createMemoizedSelector(
|
||||
[stateSelector, activeTabNameSelector],
|
||||
({ canvas }, activeTabName) => {
|
||||
const { futureLayerStates } = canvas;
|
||||
|
||||
return {
|
||||
canRedo: futureLayerStates.length > 0,
|
||||
activeTabName,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const IAICanvasRedoButton = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { canRedo, activeTabName } = useAppSelector(canvasRedoSelector);
|
||||
const canRedo = useAppSelector((s) => s.canvas.futureLayerStates.length > 0);
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvCheckbox } from 'common/components/InvCheckbox/wrapper';
|
||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||
@@ -28,50 +26,28 @@ import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaWrench } from 'react-icons/fa';
|
||||
|
||||
export const canvasControlsSelector = createMemoizedSelector(
|
||||
[stateSelector],
|
||||
({ canvas }) => {
|
||||
const {
|
||||
shouldAutoSave,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
shouldDarkenOutsideBoundingBox,
|
||||
shouldShowCanvasDebugInfo,
|
||||
shouldShowGrid,
|
||||
shouldShowIntermediates,
|
||||
shouldSnapToGrid,
|
||||
shouldRestrictStrokesToBox,
|
||||
shouldAntialias,
|
||||
} = canvas;
|
||||
|
||||
return {
|
||||
shouldAutoSave,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
shouldDarkenOutsideBoundingBox,
|
||||
shouldShowCanvasDebugInfo,
|
||||
shouldShowGrid,
|
||||
shouldShowIntermediates,
|
||||
shouldSnapToGrid,
|
||||
shouldRestrictStrokesToBox,
|
||||
shouldAntialias,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const IAICanvasSettingsButtonPopover = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
shouldAutoSave,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
shouldDarkenOutsideBoundingBox,
|
||||
shouldShowCanvasDebugInfo,
|
||||
shouldShowGrid,
|
||||
shouldShowIntermediates,
|
||||
shouldSnapToGrid,
|
||||
shouldRestrictStrokesToBox,
|
||||
shouldAntialias,
|
||||
} = useAppSelector(canvasControlsSelector);
|
||||
const shouldAutoSave = useAppSelector((s) => s.canvas.shouldAutoSave);
|
||||
const shouldCropToBoundingBoxOnSave = useAppSelector(
|
||||
(s) => s.canvas.shouldCropToBoundingBoxOnSave
|
||||
);
|
||||
const shouldDarkenOutsideBoundingBox = useAppSelector(
|
||||
(s) => s.canvas.shouldDarkenOutsideBoundingBox
|
||||
);
|
||||
const shouldShowCanvasDebugInfo = useAppSelector(
|
||||
(s) => s.canvas.shouldShowCanvasDebugInfo
|
||||
);
|
||||
const shouldShowGrid = useAppSelector((s) => s.canvas.shouldShowGrid);
|
||||
const shouldShowIntermediates = useAppSelector(
|
||||
(s) => s.canvas.shouldShowIntermediates
|
||||
);
|
||||
const shouldSnapToGrid = useAppSelector((s) => s.canvas.shouldSnapToGrid);
|
||||
const shouldRestrictStrokesToBox = useAppSelector(
|
||||
(s) => s.canvas.shouldRestrictStrokesToBox
|
||||
);
|
||||
const shouldAntialias = useAppSelector((s) => s.canvas.shouldAntialias);
|
||||
|
||||
useHotkeys(
|
||||
['n'],
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup';
|
||||
@@ -36,23 +34,12 @@ import {
|
||||
FaTimes,
|
||||
} from 'react-icons/fa';
|
||||
|
||||
export const selector = createMemoizedSelector(
|
||||
[stateSelector, isStagingSelector],
|
||||
({ canvas }, isStaging) => {
|
||||
const { tool, brushColor, brushSize } = canvas;
|
||||
|
||||
return {
|
||||
tool,
|
||||
isStaging,
|
||||
brushColor,
|
||||
brushSize,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const IAICanvasToolChooserOptions = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { tool, brushColor, brushSize, isStaging } = useAppSelector(selector);
|
||||
const tool = useAppSelector((s) => s.canvas.tool);
|
||||
const brushColor = useAppSelector((s) => s.canvas.brushColor);
|
||||
const brushSize = useAppSelector((s) => s.canvas.brushSize);
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const { t } = useTranslation();
|
||||
|
||||
useHotkeys(
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup';
|
||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||
@@ -48,27 +46,13 @@ import IAICanvasSettingsButtonPopover from './IAICanvasSettingsButtonPopover';
|
||||
import IAICanvasToolChooserOptions from './IAICanvasToolChooserOptions';
|
||||
import IAICanvasUndoButton from './IAICanvasUndoButton';
|
||||
|
||||
export const selector = createMemoizedSelector(
|
||||
[stateSelector, isStagingSelector],
|
||||
({ canvas }, isStaging) => {
|
||||
const { tool, shouldCropToBoundingBoxOnSave, layer, isMaskEnabled } =
|
||||
canvas;
|
||||
|
||||
return {
|
||||
isStaging,
|
||||
isMaskEnabled,
|
||||
tool,
|
||||
layer,
|
||||
shouldCropToBoundingBoxOnSave,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const IAICanvasToolbar = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { isStaging, isMaskEnabled, layer, tool } = useAppSelector(selector);
|
||||
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
|
||||
const layer = useAppSelector((s) => s.canvas.layer);
|
||||
const tool = useAppSelector((s) => s.canvas.tool);
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const canvasBaseLayer = getCanvasBaseLayer();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { isClipboardAPIAvailable } = useCopyImageToClipboard();
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
||||
import { undo } from 'features/canvas/store/canvasSlice';
|
||||
@@ -9,24 +7,11 @@ import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaUndo } from 'react-icons/fa';
|
||||
|
||||
const canvasUndoSelector = createMemoizedSelector(
|
||||
[stateSelector, activeTabNameSelector],
|
||||
({ canvas }, activeTabName) => {
|
||||
const { pastLayerStates } = canvas;
|
||||
|
||||
return {
|
||||
canUndo: pastLayerStates.length > 0,
|
||||
activeTabName,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const IAICanvasUndoButton = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { canUndo, activeTabName } = useAppSelector(canvasUndoSelector);
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
const canUndo = useAppSelector((s) => s.canvas.pastLayerStates.length > 0);
|
||||
|
||||
const handleUndo = useCallback(() => {
|
||||
dispatch(undo());
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useCallback } from 'react';
|
||||
const useCanvasDrag = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const tool = useAppSelector((state) => state.canvas.tool);
|
||||
const tool = useAppSelector((s) => s.canvas.tool);
|
||||
const isMovingBoundingBox = useStore($isMovingBoundingBox);
|
||||
const handleDragStart = useCallback(() => {
|
||||
if (!((tool === 'move' || isStaging) && !isMovingBoundingBox)) {
|
||||
|
||||
@@ -6,18 +6,18 @@ import { useEffect, useState } from 'react';
|
||||
import { useDebounce } from 'react-use';
|
||||
|
||||
export const useCanvasGenerationMode = () => {
|
||||
const layerState = useAppSelector((state) => state.canvas.layerState);
|
||||
const layerState = useAppSelector((s) => s.canvas.layerState);
|
||||
|
||||
const boundingBoxCoordinates = useAppSelector(
|
||||
(state) => state.canvas.boundingBoxCoordinates
|
||||
(s) => s.canvas.boundingBoxCoordinates
|
||||
);
|
||||
const boundingBoxDimensions = useAppSelector(
|
||||
(state) => state.canvas.boundingBoxDimensions
|
||||
(s) => s.canvas.boundingBoxDimensions
|
||||
);
|
||||
const isMaskEnabled = useAppSelector((state) => state.canvas.isMaskEnabled);
|
||||
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
|
||||
|
||||
const shouldPreserveMaskedArea = useAppSelector(
|
||||
(state) => state.canvas.shouldPreserveMaskedArea
|
||||
(s) => s.canvas.shouldPreserveMaskedArea
|
||||
);
|
||||
const [generationMode, setGenerationMode] = useState<
|
||||
GenerationMode | undefined
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
resetCanvasInteractionState,
|
||||
@@ -16,49 +14,24 @@ import {
|
||||
import type { CanvasTool } from 'features/canvas/store/canvasTypes';
|
||||
import { getCanvasStage } from 'features/canvas/util/konvaInstanceProvider';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { useRef } from 'react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[stateSelector, activeTabNameSelector, isStagingSelector],
|
||||
({ canvas }, activeTabName, isStaging) => {
|
||||
const {
|
||||
shouldLockBoundingBox,
|
||||
shouldShowBoundingBox,
|
||||
tool,
|
||||
isMaskEnabled,
|
||||
shouldSnapToGrid,
|
||||
} = canvas;
|
||||
|
||||
return {
|
||||
activeTabName,
|
||||
shouldLockBoundingBox,
|
||||
shouldShowBoundingBox,
|
||||
tool,
|
||||
isStaging,
|
||||
isMaskEnabled,
|
||||
shouldSnapToGrid,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const useInpaintingCanvasHotkeys = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
activeTabName,
|
||||
shouldShowBoundingBox,
|
||||
tool,
|
||||
isStaging,
|
||||
isMaskEnabled,
|
||||
shouldSnapToGrid,
|
||||
} = useAppSelector(selector);
|
||||
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
const shouldShowBoundingBox = useAppSelector(
|
||||
(s) => s.canvas.shouldShowBoundingBox
|
||||
);
|
||||
const tool = useAppSelector((s) => s.canvas.tool);
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
|
||||
const shouldSnapToGrid = useAppSelector((s) => s.canvas.shouldSnapToGrid);
|
||||
const previousToolRef = useRef<CanvasTool | null>(null);
|
||||
|
||||
const canvasStage = getCanvasStage();
|
||||
|
||||
// Beta Keys
|
||||
const handleClearMask = () => dispatch(clearMask());
|
||||
const handleClearMask = useCallback(() => dispatch(clearMask()), [dispatch]);
|
||||
|
||||
useHotkeys(
|
||||
['shift+c'],
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
setIsDrawing,
|
||||
@@ -8,7 +6,6 @@ import {
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { addLine } from 'features/canvas/store/canvasSlice';
|
||||
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import type Konva from 'konva';
|
||||
import type { KonvaEventObject } from 'konva/lib/Node';
|
||||
import type { MutableRefObject } from 'react';
|
||||
@@ -16,21 +13,10 @@ import { useCallback } from 'react';
|
||||
|
||||
import useColorPicker from './useColorUnderCursor';
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[activeTabNameSelector, stateSelector, isStagingSelector],
|
||||
(activeTabName, { canvas }, isStaging) => {
|
||||
const { tool } = canvas;
|
||||
return {
|
||||
tool,
|
||||
activeTabName,
|
||||
isStaging,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const useCanvasMouseDown = (stageRef: MutableRefObject<Konva.Stage | null>) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { tool, isStaging } = useAppSelector(selector);
|
||||
const tool = useAppSelector((s) => s.canvas.tool);
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const { commitColorUnderCursor } = useColorPicker();
|
||||
|
||||
return useCallback(
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
$isDrawing,
|
||||
@@ -9,7 +7,6 @@ import {
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { addPointToCurrentLine } from 'features/canvas/store/canvasSlice';
|
||||
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import type Konva from 'konva';
|
||||
import type { Vector2d } from 'konva/lib/types';
|
||||
import type { MutableRefObject } from 'react';
|
||||
@@ -17,18 +14,6 @@ import { useCallback } from 'react';
|
||||
|
||||
import useColorPicker from './useColorUnderCursor';
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[activeTabNameSelector, stateSelector, isStagingSelector],
|
||||
(activeTabName, { canvas }, isStaging) => {
|
||||
const { tool } = canvas;
|
||||
return {
|
||||
tool,
|
||||
activeTabName,
|
||||
isStaging,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const useCanvasMouseMove = (
|
||||
stageRef: MutableRefObject<Konva.Stage | null>,
|
||||
didMouseMoveRef: MutableRefObject<boolean>,
|
||||
@@ -36,7 +21,8 @@ const useCanvasMouseMove = (
|
||||
) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const isDrawing = useStore($isDrawing);
|
||||
const { tool, isStaging } = useAppSelector(selector);
|
||||
const tool = useAppSelector((s) => s.canvas.tool);
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
const { updateColorUnderCursor } = useColorPicker();
|
||||
|
||||
return useCallback(() => {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
$isDrawing,
|
||||
@@ -8,32 +6,20 @@ import {
|
||||
setIsMovingStage,
|
||||
} from 'features/canvas/store/canvasNanostore';
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
// addPointToCurrentEraserLine,
|
||||
addPointToCurrentLine,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { addPointToCurrentLine } from 'features/canvas/store/canvasSlice';
|
||||
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
|
||||
import type Konva from 'konva';
|
||||
import type { MutableRefObject } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[stateSelector, isStagingSelector],
|
||||
({ canvas }, isStaging) => {
|
||||
return {
|
||||
tool: canvas.tool,
|
||||
isStaging,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const useCanvasMouseUp = (
|
||||
stageRef: MutableRefObject<Konva.Stage | null>,
|
||||
didMouseMoveRef: MutableRefObject<boolean>
|
||||
) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const isDrawing = useStore($isDrawing);
|
||||
const { tool, isStaging } = useAppSelector(selector);
|
||||
const tool = useAppSelector((s) => s.canvas.tool);
|
||||
const isStaging = useAppSelector(isStagingSelector);
|
||||
|
||||
return useCallback(() => {
|
||||
if (tool === 'move' || isStaging) {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { $isMoveStageKeyHeld } from 'features/canvas/store/canvasNanostore';
|
||||
import {
|
||||
@@ -18,14 +16,9 @@ import { clamp } from 'lodash-es';
|
||||
import type { MutableRefObject } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[stateSelector],
|
||||
(state) => state.canvas.stageScale
|
||||
);
|
||||
|
||||
const useCanvasWheel = (stageRef: MutableRefObject<Konva.Stage | null>) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const stageScale = useAppSelector(selector);
|
||||
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||
const isMoveStageKeyHeld = useStore($isMoveStageKeyHeld);
|
||||
|
||||
return useCallback(
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
|
||||
import type { CanvasImage } from './canvasTypes';
|
||||
import { selectCanvasSlice } from './canvasSlice';
|
||||
import { isCanvasBaseImage } from './canvasTypes';
|
||||
|
||||
export const isStagingSelector = createMemoizedSelector(
|
||||
[stateSelector],
|
||||
({ canvas }) =>
|
||||
export const isStagingSelector = createSelector(
|
||||
selectCanvasSlice,
|
||||
(canvas) =>
|
||||
canvas.batchIds.length > 0 ||
|
||||
canvas.layerState.stagingArea.images.length > 0
|
||||
);
|
||||
|
||||
export const initialCanvasImageSelector = (
|
||||
state: RootState
|
||||
): CanvasImage | undefined =>
|
||||
state.canvas.layerState.objects.find(isCanvasBaseImage);
|
||||
export const initialCanvasImageSelector = createMemoizedSelector(
|
||||
selectCanvasSlice,
|
||||
(canvas) => canvas.layerState.objects.find(isCanvasBaseImage)
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import {
|
||||
roundDownToMultiple,
|
||||
roundToMultiple,
|
||||
@@ -781,3 +782,5 @@ export const {
|
||||
} = canvasSlice.actions;
|
||||
|
||||
export default canvasSlice.reducer;
|
||||
|
||||
export const selectCanvasSlice = (state: RootState) => state.canvas;
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
export const getColoredMaskSVG = (color: string) => {
|
||||
return `data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="60px" height="60px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||
<g transform="matrix(0.5,0,0,0.5,0,0)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,2.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,7.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,10)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,12.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,15)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,17.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,20)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,22.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,25)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,27.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,30)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-2.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-7.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-10)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-12.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-15)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-17.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-20)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-22.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-25)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-27.5)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.5,0,0,0.5,0,-30)">
|
||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
</svg>`.replaceAll('black', color);
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvConfirmationAlertDialog } from 'common/components/InvConfirmationAlertDialog/InvConfirmationAlertDialog';
|
||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||
@@ -13,6 +12,7 @@ import { InvText } from 'common/components/InvText/wrapper';
|
||||
import {
|
||||
changeBoardReset,
|
||||
isModalOpenChanged,
|
||||
selectChangeBoardModalSlice,
|
||||
} from 'features/changeBoardModal/store/slice';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -22,23 +22,17 @@ import {
|
||||
useRemoveImagesFromBoardMutation,
|
||||
} from 'services/api/endpoints/images';
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[stateSelector],
|
||||
({ changeBoardModal }) => {
|
||||
const { isModalOpen, imagesToChange } = changeBoardModal;
|
||||
|
||||
return {
|
||||
isModalOpen,
|
||||
imagesToChange,
|
||||
};
|
||||
}
|
||||
const selectImagesToChange = createMemoizedSelector(
|
||||
selectChangeBoardModalSlice,
|
||||
(changeBoardModal) => changeBoardModal.imagesToChange
|
||||
);
|
||||
|
||||
const ChangeBoardModal = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [selectedBoard, setSelectedBoard] = useState<string | null>();
|
||||
const { data: boards, isFetching } = useListAllBoardsQuery();
|
||||
const { imagesToChange, isModalOpen } = useAppSelector(selector);
|
||||
const isModalOpen = useAppSelector((s) => s.changeBoardModal.isModalOpen);
|
||||
const imagesToChange = useAppSelector(selectImagesToChange);
|
||||
const [addImagesToBoard] = useAddImagesToBoardMutation();
|
||||
const [removeImagesFromBoard] = useRemoveImagesFromBoardMutation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
import { initialState } from './initialState';
|
||||
@@ -25,3 +26,6 @@ export const { isModalOpenChanged, imagesToChangeSelected, changeBoardReset } =
|
||||
changeBoardModal.actions;
|
||||
|
||||
export default changeBoardModal.reducer;
|
||||
|
||||
export const selectChangeBoardModalSlice = (state: RootState) =>
|
||||
state.changeBoardModal;
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import { Box, Flex, Spinner } from '@chakra-ui/react';
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||
@@ -10,7 +9,10 @@ import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||
import { useControlAdapterControlImage } from 'features/controlAdapters/hooks/useControlAdapterControlImage';
|
||||
import { useControlAdapterProcessedControlImage } from 'features/controlAdapters/hooks/useControlAdapterProcessedControlImage';
|
||||
import { useControlAdapterProcessorType } from 'features/controlAdapters/hooks/useControlAdapterProcessorType';
|
||||
import { controlAdapterImageChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
controlAdapterImageChanged,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import type {
|
||||
TypesafeDraggableData,
|
||||
TypesafeDroppableData,
|
||||
@@ -37,36 +39,22 @@ type Props = {
|
||||
isSmall?: boolean;
|
||||
};
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[stateSelector, activeTabNameSelector, selectOptimalDimension],
|
||||
({ controlAdapters, gallery, system }, activeTabName, optimalDimension) => {
|
||||
const { pendingControlImages } = controlAdapters;
|
||||
const { autoAddBoardId } = gallery;
|
||||
const { isConnected } = system;
|
||||
|
||||
return {
|
||||
pendingControlImages,
|
||||
autoAddBoardId,
|
||||
isConnected,
|
||||
activeTabName,
|
||||
optimalDimension,
|
||||
};
|
||||
}
|
||||
const selectPendingControlImages = createMemoizedSelector(
|
||||
selectControlAdaptersSlice,
|
||||
(controlAdapters) => controlAdapters.pendingControlImages
|
||||
);
|
||||
|
||||
const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const controlImageName = useControlAdapterControlImage(id);
|
||||
const processedControlImageName = useControlAdapterProcessedControlImage(id);
|
||||
const processorType = useControlAdapterProcessorType(id);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
pendingControlImages,
|
||||
autoAddBoardId,
|
||||
isConnected,
|
||||
activeTabName,
|
||||
optimalDimension,
|
||||
} = useAppSelector(selector);
|
||||
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
|
||||
const isConnected = useAppSelector((s) => s.system.isConnected);
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||
const pendingControlImages = useAppSelector(selectPendingControlImages);
|
||||
|
||||
const [isMouseOverImage, setIsMouseOverImage] = useState(false);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||
import { InvSelect } from 'common/components/InvSelect/InvSelect';
|
||||
@@ -10,6 +9,7 @@ import { useControlAdapterModel } from 'features/controlAdapters/hooks/useContro
|
||||
import { useControlAdapterModelEntities } from 'features/controlAdapters/hooks/useControlAdapterModelEntities';
|
||||
import { useControlAdapterType } from 'features/controlAdapters/hooks/useControlAdapterType';
|
||||
import { controlAdapterModelChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
||||
import { pick } from 'lodash-es';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -24,10 +24,10 @@ type ParamControlAdapterModelProps = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const selector = createMemoizedSelector(stateSelector, ({ generation }) => {
|
||||
const { model } = generation;
|
||||
return { mainModel: model };
|
||||
});
|
||||
const selectMainModel = createMemoizedSelector(
|
||||
selectGenerationSlice,
|
||||
(generation) => generation.model
|
||||
);
|
||||
|
||||
const ParamControlAdapterModel = ({ id }: ParamControlAdapterModelProps) => {
|
||||
const isEnabled = useControlAdapterIsEnabled(id);
|
||||
@@ -35,9 +35,9 @@ const ParamControlAdapterModel = ({ id }: ParamControlAdapterModelProps) => {
|
||||
const model = useControlAdapterModel(id);
|
||||
const dispatch = useAppDispatch();
|
||||
const currentBaseModel = useAppSelector(
|
||||
(state) => state.generation.model?.base_model
|
||||
(s) => s.generation.model?.base_model
|
||||
);
|
||||
const { mainModel } = useAppSelector(selector);
|
||||
const mainModel = useAppSelector(selectMainModel);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const models = useControlAdapterModelEntities(controlAdapterType);
|
||||
|
||||
@@ -20,7 +20,7 @@ type Props = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const selector = createMemoizedSelector(configSelector, (config) => {
|
||||
const selectOptions = createMemoizedSelector(configSelector, (config) => {
|
||||
const options: InvSelectOption[] = map(CONTROLNET_PROCESSORS, (p) => ({
|
||||
value: p.type,
|
||||
label: p.label,
|
||||
@@ -47,7 +47,7 @@ const ParamControlAdapterProcessorSelect = ({ id }: Props) => {
|
||||
const isEnabled = useControlAdapterIsEnabled(id);
|
||||
const processorNode = useControlAdapterProcessorNode(id);
|
||||
const dispatch = useAppDispatch();
|
||||
const options = useAppSelector(selector);
|
||||
const options = useAppSelector(selectOptions);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onChange = useCallback<InvSelectOnChange>(
|
||||
|
||||
@@ -6,9 +6,7 @@ import { useCallback, useMemo } from 'react';
|
||||
import { useControlAdapterModels } from './useControlAdapterModels';
|
||||
|
||||
export const useAddControlAdapter = (type: ControlAdapterType) => {
|
||||
const baseModel = useAppSelector(
|
||||
(state) => state.generation.model?.base_model
|
||||
);
|
||||
const baseModel = useAppSelector((s) => s.generation.model?.base_model);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const models = useControlAdapterModels(type);
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapter = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(stateSelector, ({ controlAdapters }) =>
|
||||
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) =>
|
||||
selectControlAdapterById(controlAdapters, id)
|
||||
),
|
||||
[id]
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterBeginEndStepPct = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
|
||||
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => {
|
||||
const cn = selectControlAdapterById(controlAdapters, id);
|
||||
return cn
|
||||
? {
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterControlImage = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(
|
||||
stateSelector,
|
||||
({ controlAdapters }) =>
|
||||
createSelector(
|
||||
selectControlAdaptersSlice,
|
||||
(controlAdapters) =>
|
||||
selectControlAdapterById(controlAdapters, id)?.controlImage
|
||||
),
|
||||
[id]
|
||||
);
|
||||
|
||||
const weight = useAppSelector(selector);
|
||||
const controlImageName = useAppSelector(selector);
|
||||
|
||||
return weight;
|
||||
return controlImageName;
|
||||
};
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { isControlNet } from 'features/controlAdapters/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterControlMode = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
|
||||
createSelector(selectControlAdaptersSlice, (controlAdapters) => {
|
||||
const ca = selectControlAdapterById(controlAdapters, id);
|
||||
if (ca && isControlNet(ca)) {
|
||||
return ca.controlMode;
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterIsEnabled = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(
|
||||
stateSelector,
|
||||
({ controlAdapters }) =>
|
||||
createSelector(
|
||||
selectControlAdaptersSlice,
|
||||
(controlAdapters) =>
|
||||
selectControlAdapterById(controlAdapters, id)?.isEnabled ?? false
|
||||
),
|
||||
[id]
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterModel = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(
|
||||
stateSelector,
|
||||
({ controlAdapters }) =>
|
||||
selectControlAdaptersSlice,
|
||||
(controlAdapters) =>
|
||||
selectControlAdapterById(controlAdapters, id)?.model
|
||||
),
|
||||
[id]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { ControlAdapterType } from 'features/controlAdapters/store/types';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
controlNetModelsAdapter,
|
||||
ipAdapterModelsAdapter,
|
||||
t2iAdapterModelsAdapter,
|
||||
controlNetModelsAdapterSelectors,
|
||||
ipAdapterModelsAdapterSelectors,
|
||||
t2iAdapterModelsAdapterSelectors,
|
||||
useGetControlNetModelsQuery,
|
||||
useGetIPAdapterModelsQuery,
|
||||
useGetT2IAdapterModelsQuery,
|
||||
@@ -14,7 +14,7 @@ export const useControlAdapterModels = (type?: ControlAdapterType) => {
|
||||
const controlNetModels = useMemo(
|
||||
() =>
|
||||
controlNetModelsData
|
||||
? controlNetModelsAdapter.getSelectors().selectAll(controlNetModelsData)
|
||||
? controlNetModelsAdapterSelectors.selectAll(controlNetModelsData)
|
||||
: [],
|
||||
[controlNetModelsData]
|
||||
);
|
||||
@@ -23,7 +23,7 @@ export const useControlAdapterModels = (type?: ControlAdapterType) => {
|
||||
const t2iAdapterModels = useMemo(
|
||||
() =>
|
||||
t2iAdapterModelsData
|
||||
? t2iAdapterModelsAdapter.getSelectors().selectAll(t2iAdapterModelsData)
|
||||
? t2iAdapterModelsAdapterSelectors.selectAll(t2iAdapterModelsData)
|
||||
: [],
|
||||
[t2iAdapterModelsData]
|
||||
);
|
||||
@@ -31,7 +31,7 @@ export const useControlAdapterModels = (type?: ControlAdapterType) => {
|
||||
const ipAdapterModels = useMemo(
|
||||
() =>
|
||||
ipAdapterModelsData
|
||||
? ipAdapterModelsAdapter.getSelectors().selectAll(ipAdapterModelsData)
|
||||
? ipAdapterModelsAdapterSelectors.selectAll(ipAdapterModelsData)
|
||||
: [],
|
||||
[ipAdapterModelsData]
|
||||
);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterProcessedControlImage = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
|
||||
createSelector(selectControlAdaptersSlice, (controlAdapters) => {
|
||||
const ca = selectControlAdapterById(controlAdapters, id);
|
||||
|
||||
return ca && isControlNetOrT2IAdapter(ca)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterProcessorNode = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
|
||||
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => {
|
||||
const ca = selectControlAdapterById(controlAdapters, id);
|
||||
|
||||
return ca && isControlNetOrT2IAdapter(ca)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterProcessorType = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
|
||||
createSelector(selectControlAdaptersSlice, (controlAdapters) => {
|
||||
const ca = selectControlAdapterById(controlAdapters, id);
|
||||
|
||||
return ca && isControlNetOrT2IAdapter(ca)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterResizeMode = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
|
||||
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => {
|
||||
const ca = selectControlAdapterById(controlAdapters, id);
|
||||
if (ca && isControlNetOrT2IAdapter(ca)) {
|
||||
return ca.resizeMode;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterShouldAutoConfig = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(stateSelector, ({ controlAdapters }) => {
|
||||
createSelector(selectControlAdaptersSlice, (controlAdapters) => {
|
||||
const ca = selectControlAdapterById(controlAdapters, id);
|
||||
if (ca && isControlNetOrT2IAdapter(ca)) {
|
||||
return ca.shouldAutoConfig;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterType = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(
|
||||
stateSelector,
|
||||
({ controlAdapters }) =>
|
||||
selectControlAdapterById(controlAdapters, id)?.type
|
||||
selectControlAdaptersSlice,
|
||||
(controlAdapters) => selectControlAdapterById(controlAdapters, id)?.type
|
||||
),
|
||||
[id]
|
||||
);
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectControlAdapterById } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
selectControlAdapterById,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useControlAdapterWeight = (id: string) => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(
|
||||
stateSelector,
|
||||
({ controlAdapters }) =>
|
||||
createSelector(
|
||||
selectControlAdaptersSlice,
|
||||
(controlAdapters) =>
|
||||
selectControlAdapterById(controlAdapters, id)?.weight
|
||||
),
|
||||
[id]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { PayloadAction, Update } from '@reduxjs/toolkit';
|
||||
import { createEntityAdapter, createSlice, isAnyOf } from '@reduxjs/toolkit';
|
||||
import { getSelectorsOptions } from 'app/store/createMemoizedSelector';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { buildControlAdapter } from 'features/controlAdapters/util/buildControlAdapter';
|
||||
import type {
|
||||
ParameterControlNetModel,
|
||||
@@ -36,6 +38,10 @@ import {
|
||||
export const caAdapter = createEntityAdapter<ControlAdapterConfig, string>({
|
||||
selectId: (ca) => ca.id,
|
||||
});
|
||||
export const caAdapterSelectors = caAdapter.getSelectors(
|
||||
undefined,
|
||||
getSelectorsOptions
|
||||
);
|
||||
|
||||
export const {
|
||||
selectById: selectControlAdapterById,
|
||||
@@ -43,7 +49,7 @@ export const {
|
||||
selectEntities: selectControlAdapterEntities,
|
||||
selectIds: selectControlAdapterIds,
|
||||
selectTotal: selectControlAdapterTotal,
|
||||
} = caAdapter.getSelectors();
|
||||
} = caAdapterSelectors;
|
||||
|
||||
export const initialControlAdapterState: ControlAdaptersState =
|
||||
caAdapter.getInitialState<{
|
||||
@@ -484,3 +490,6 @@ export const isAnyControlAdapterAdded = isAnyOf(
|
||||
controlAdapterAddedFromImage,
|
||||
controlAdapterRecalled
|
||||
);
|
||||
|
||||
export const selectControlAdaptersSlice = (state: RootState) =>
|
||||
state.controlAdapters;
|
||||
|
||||
@@ -12,7 +12,7 @@ type DeleteImageButtonProps = Omit<InvIconButtonProps, 'aria-label'> & {
|
||||
export const DeleteImageButton = memo((props: DeleteImageButtonProps) => {
|
||||
const { onClick, isDisabled } = props;
|
||||
const { t } = useTranslation();
|
||||
const isConnected = useAppSelector((state) => state.system.isConnected);
|
||||
const isConnected = useAppSelector((s) => s.system.isConnected);
|
||||
|
||||
return (
|
||||
<InvIconButton
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Divider, Flex } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvConfirmationAlertDialog } from 'common/components/InvConfirmationAlertDialog/InvConfirmationAlertDialog';
|
||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||
import { InvSwitch } from 'common/components/InvSwitch/wrapper';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
||||
import {
|
||||
getImageUsage,
|
||||
@@ -14,8 +15,11 @@ import {
|
||||
import {
|
||||
imageDeletionCanceled,
|
||||
isModalOpenChanged,
|
||||
selectDeleteImageModalSlice,
|
||||
} from 'features/deleteImageModal/store/slice';
|
||||
import type { ImageUsage } from 'features/deleteImageModal/store/types';
|
||||
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
||||
import { setShouldConfirmOnDelete } from 'features/system/store/systemSlice';
|
||||
import { some } from 'lodash-es';
|
||||
import type { ChangeEvent } from 'react';
|
||||
@@ -24,16 +28,27 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import ImageUsageMessage from './ImageUsageMessage';
|
||||
|
||||
const selector = createMemoizedSelector(
|
||||
[stateSelector, selectImageUsage],
|
||||
(state, imagesUsage) => {
|
||||
const { system, config, deleteImageModal } = state;
|
||||
const { shouldConfirmOnDelete } = system;
|
||||
const { canRestoreDeletedImagesFromBin } = config;
|
||||
const { imagesToDelete, isModalOpen } = deleteImageModal;
|
||||
const selectImageUsages = createMemoizedSelector(
|
||||
[
|
||||
selectDeleteImageModalSlice,
|
||||
selectGenerationSlice,
|
||||
selectCanvasSlice,
|
||||
selectNodesSlice,
|
||||
selectControlAdaptersSlice,
|
||||
selectImageUsage,
|
||||
],
|
||||
(
|
||||
deleteImageModal,
|
||||
generation,
|
||||
canvas,
|
||||
nodes,
|
||||
controlAdapters,
|
||||
imagesUsage
|
||||
) => {
|
||||
const { imagesToDelete } = deleteImageModal;
|
||||
|
||||
const allImageUsage = (imagesToDelete ?? []).map(({ image_name }) =>
|
||||
getImageUsage(state, image_name)
|
||||
getImageUsage(generation, canvas, nodes, controlAdapters, image_name)
|
||||
);
|
||||
|
||||
const imageUsageSummary: ImageUsage = {
|
||||
@@ -44,11 +59,8 @@ const selector = createMemoizedSelector(
|
||||
};
|
||||
|
||||
return {
|
||||
shouldConfirmOnDelete,
|
||||
canRestoreDeletedImagesFromBin,
|
||||
imagesToDelete,
|
||||
imagesUsage,
|
||||
isModalOpen,
|
||||
imageUsageSummary,
|
||||
};
|
||||
}
|
||||
@@ -57,15 +69,15 @@ const selector = createMemoizedSelector(
|
||||
const DeleteImageModal = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
shouldConfirmOnDelete,
|
||||
canRestoreDeletedImagesFromBin,
|
||||
imagesToDelete,
|
||||
imagesUsage,
|
||||
isModalOpen,
|
||||
imageUsageSummary,
|
||||
} = useAppSelector(selector);
|
||||
const shouldConfirmOnDelete = useAppSelector(
|
||||
(s) => s.system.shouldConfirmOnDelete
|
||||
);
|
||||
const canRestoreDeletedImagesFromBin = useAppSelector(
|
||||
(s) => s.config.canRestoreDeletedImagesFromBin
|
||||
);
|
||||
const isModalOpen = useAppSelector((s) => s.deleteImageModal.isModalOpen);
|
||||
const { imagesToDelete, imagesUsage, imageUsageSummary } =
|
||||
useAppSelector(selectImageUsages);
|
||||
|
||||
const handleChangeShouldConfirmOnDelete = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) =>
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { selectControlAdapterAll } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
import type { CanvasState } from 'features/canvas/store/canvasTypes';
|
||||
import {
|
||||
selectControlAdapterAll,
|
||||
selectControlAdaptersSlice,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import type { ControlAdaptersState } from 'features/controlAdapters/store/types';
|
||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||
import { selectDeleteImageModalSlice } from 'features/deleteImageModal/store/slice';
|
||||
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import type { NodesState } from 'features/nodes/store/types';
|
||||
import { isImageFieldInputInstance } from 'features/nodes/types/field';
|
||||
import { isInvocationNode } from 'features/nodes/types/invocation';
|
||||
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
||||
import type { GenerationState } from 'features/parameters/store/types';
|
||||
import { some } from 'lodash-es';
|
||||
|
||||
import type { ImageUsage } from './types';
|
||||
|
||||
export const getImageUsage = (state: RootState, image_name: string) => {
|
||||
const { generation, canvas, nodes, controlAdapters } = state;
|
||||
export const getImageUsage = (
|
||||
generation: GenerationState,
|
||||
canvas: CanvasState,
|
||||
nodes: NodesState,
|
||||
controlAdapters: ControlAdaptersState,
|
||||
image_name: string
|
||||
) => {
|
||||
const isInitialImage = generation.initialImage?.imageName === image_name;
|
||||
|
||||
const isCanvasImage = canvas.layerState.objects.some(
|
||||
@@ -42,16 +57,20 @@ export const getImageUsage = (state: RootState, image_name: string) => {
|
||||
};
|
||||
|
||||
export const selectImageUsage = createMemoizedSelector(
|
||||
[(state: RootState) => state],
|
||||
(state) => {
|
||||
const { imagesToDelete } = state.deleteImageModal;
|
||||
selectDeleteImageModalSlice,
|
||||
selectGenerationSlice,
|
||||
selectCanvasSlice,
|
||||
selectNodesSlice,
|
||||
selectControlAdaptersSlice,
|
||||
(deleteImageModal, generation, canvas, nodes, controlAdapters) => {
|
||||
const { imagesToDelete } = deleteImageModal;
|
||||
|
||||
if (!imagesToDelete.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const imagesUsage = imagesToDelete.map((i) =>
|
||||
getImageUsage(state, i.image_name)
|
||||
getImageUsage(generation, canvas, nodes, controlAdapters, i.image_name)
|
||||
);
|
||||
|
||||
return imagesUsage;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
import { initialDeleteImageState } from './initialState';
|
||||
@@ -28,3 +29,6 @@ export const {
|
||||
} = deleteImageModal.actions;
|
||||
|
||||
export default deleteImageModal.reducer;
|
||||
|
||||
export const selectDeleteImageModalSlice = (state: RootState) =>
|
||||
state.deleteImageModal;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { Modifier } from '@dnd-kit/core';
|
||||
import { getEventCoordinates } from '@dnd-kit/utilities';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
const selectZoom = createMemoizedSelector(
|
||||
[stateSelector, activeTabNameSelector],
|
||||
({ nodes }, activeTabName) =>
|
||||
const selectZoom = createSelector(
|
||||
[selectNodesSlice, activeTabNameSelector],
|
||||
(nodes, activeTabName) =>
|
||||
activeTabName === 'nodes' ? nodes.viewport.zoom : 1
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||
import { InvSwitch } from 'common/components/InvSwitch/wrapper';
|
||||
@@ -7,14 +5,8 @@ import { combinatorialToggled } from 'features/dynamicPrompts/store/dynamicPromp
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createMemoizedSelector(stateSelector, (state) => {
|
||||
const { combinatorial } = state.dynamicPrompts;
|
||||
|
||||
return { combinatorial };
|
||||
});
|
||||
|
||||
const ParamDynamicPromptsCombinatorial = () => {
|
||||
const { combinatorial } = useAppSelector(selector);
|
||||
const combinatorial = useAppSelector((s) => s.dynamicPrompts.combinatorial);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
||||
@@ -7,24 +5,19 @@ import { maxPromptsChanged } from 'features/dynamicPrompts/store/dynamicPromptsS
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createMemoizedSelector(stateSelector, (state) => {
|
||||
const { maxPrompts, combinatorial } = state.dynamicPrompts;
|
||||
const { min, sliderMax, inputMax, initial } =
|
||||
state.config.sd.dynamicPrompts.maxPrompts;
|
||||
|
||||
return {
|
||||
maxPrompts,
|
||||
min,
|
||||
sliderMax,
|
||||
inputMax,
|
||||
initial,
|
||||
isDisabled: !combinatorial,
|
||||
};
|
||||
});
|
||||
|
||||
const ParamDynamicPromptsMaxPrompts = () => {
|
||||
const { maxPrompts, min, sliderMax, inputMax, initial, isDisabled } =
|
||||
useAppSelector(selector);
|
||||
const maxPrompts = useAppSelector((s) => s.dynamicPrompts.maxPrompts);
|
||||
const min = useAppSelector((s) => s.config.sd.dynamicPrompts.maxPrompts.min);
|
||||
const sliderMax = useAppSelector(
|
||||
(s) => s.config.sd.dynamicPrompts.maxPrompts.sliderMax
|
||||
);
|
||||
const inputMax = useAppSelector(
|
||||
(s) => s.config.sd.dynamicPrompts.maxPrompts.inputMax
|
||||
);
|
||||
const initial = useAppSelector(
|
||||
(s) => s.config.sd.dynamicPrompts.maxPrompts.initial
|
||||
);
|
||||
const isDisabled = useAppSelector((s) => !s.dynamicPrompts.combinatorial);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
import type { ChakraProps } from '@chakra-ui/react';
|
||||
import { Flex, ListItem, OrderedList, Spinner } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaCircleExclamation } from 'react-icons/fa6';
|
||||
|
||||
const selector = createMemoizedSelector(stateSelector, (state) => {
|
||||
const { isLoading, isError, prompts, parsingError } = state.dynamicPrompts;
|
||||
|
||||
return {
|
||||
prompts,
|
||||
parsingError,
|
||||
isError,
|
||||
isLoading,
|
||||
};
|
||||
});
|
||||
const selectPrompts = createMemoizedSelector(
|
||||
selectDynamicPromptsSlice,
|
||||
(dynamicPrompts) => dynamicPrompts.prompts
|
||||
);
|
||||
|
||||
const listItemStyles: ChakraProps['sx'] = {
|
||||
'&::marker': { color: 'base.500' },
|
||||
@@ -28,8 +22,10 @@ const listItemStyles: ChakraProps['sx'] = {
|
||||
|
||||
const ParamDynamicPromptsPreview = () => {
|
||||
const { t } = useTranslation();
|
||||
const { prompts, parsingError, isLoading, isError } =
|
||||
useAppSelector(selector);
|
||||
const parsingError = useAppSelector((s) => s.dynamicPrompts.parsingError);
|
||||
const isError = useAppSelector((s) => s.dynamicPrompts.isError);
|
||||
const isLoading = useAppSelector((s) => s.dynamicPrompts.isLoading);
|
||||
const prompts = useAppSelector(selectPrompts);
|
||||
|
||||
const label = useMemo(() => {
|
||||
let _label = `${t('dynamicPrompts.promptsPreview')} (${prompts.length})`;
|
||||
|
||||
@@ -15,9 +15,7 @@ import { useTranslation } from 'react-i18next';
|
||||
const ParamDynamicPromptsSeedBehaviour = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const seedBehaviour = useAppSelector(
|
||||
(state) => state.dynamicPrompts.seedBehaviour
|
||||
);
|
||||
const seedBehaviour = useAppSelector((s) => s.dynamicPrompts.seedBehaviour);
|
||||
|
||||
const options = useMemo<InvSelectOption[]>(() => {
|
||||
return [
|
||||
|
||||
@@ -14,7 +14,7 @@ const loadingStyles: SystemStyleObject = {
|
||||
|
||||
export const ShowDynamicPromptsPreviewButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const isLoading = useAppSelector((state) => state.dynamicPrompts.isLoading);
|
||||
const isLoading = useAppSelector((s) => s.dynamicPrompts.isLoading);
|
||||
const { isOpen, onOpen } = useDynamicPromptsModal();
|
||||
return (
|
||||
<InvTooltip
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const zSeedBehaviour = z.enum(['PER_ITERATION', 'PER_PROMPT']);
|
||||
@@ -74,3 +75,6 @@ export const {
|
||||
} = dynamicPromptsSlice.actions;
|
||||
|
||||
export default dynamicPromptsSlice.reducer;
|
||||
|
||||
export const selectDynamicPromptsSlice = (state: RootState) =>
|
||||
state.dynamicPrompts;
|
||||
|
||||
@@ -34,7 +34,7 @@ export const EmbeddingPopover = memo((props: EmbeddingPopoverProps) => {
|
||||
p={0}
|
||||
insetBlockStart={-1}
|
||||
shadow="dark-lg"
|
||||
borderColor="blue.300"
|
||||
borderColor="invokeBlue.300"
|
||||
borderWidth="2px"
|
||||
borderStyle="solid"
|
||||
>
|
||||
|
||||
@@ -18,7 +18,7 @@ export const EmbeddingSelect = memo(
|
||||
const { t } = useTranslation();
|
||||
|
||||
const currentBaseModel = useAppSelector(
|
||||
(state) => state.generation.model?.base_model
|
||||
(s) => s.generation.model?.base_model
|
||||
);
|
||||
|
||||
const getIsDisabled = useCallback(
|
||||
|
||||
@@ -6,7 +6,7 @@ const AutoAddIcon = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Flex position="absolute" insetInlineEnd={0} top={0} p={1}>
|
||||
<Badge variant="solid" bg="blue.500">
|
||||
<Badge variant="solid" bg="invokeBlue.500">
|
||||
{t('common.auto')}
|
||||
</Badge>
|
||||
</Flex>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||
import { InvSelect } from 'common/components/InvSelect/InvSelect';
|
||||
@@ -12,19 +10,13 @@ import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
|
||||
|
||||
const selector = createMemoizedSelector([stateSelector], ({ gallery }) => {
|
||||
const { autoAddBoardId, autoAssignBoardOnClick } = gallery;
|
||||
|
||||
return {
|
||||
autoAddBoardId,
|
||||
autoAssignBoardOnClick,
|
||||
};
|
||||
});
|
||||
|
||||
const BoardAutoAddSelect = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const { autoAddBoardId, autoAssignBoardOnClick } = useAppSelector(selector);
|
||||
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
|
||||
const autoAssignBoardOnClick = useAppSelector(
|
||||
(s) => s.gallery.autoAssignBoardOnClick
|
||||
);
|
||||
const { options, hasBoards } = useListAllBoardsQuery(undefined, {
|
||||
selectFromResult: ({ data }) => {
|
||||
const options: InvSelectOption[] = [
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import type { InvContextMenuProps } from 'common/components/InvContextMenu/InvContextMenu';
|
||||
import { InvContextMenu } from 'common/components/InvContextMenu/InvContextMenu';
|
||||
import { InvMenuItem } from 'common/components/InvMenu/InvMenuItem';
|
||||
import { InvMenuList } from 'common/components/InvMenu/InvMenuList';
|
||||
import { InvMenuGroup } from 'common/components/InvMenu/wrapper';
|
||||
import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice';
|
||||
import {
|
||||
autoAddBoardIdChanged,
|
||||
selectGallerySlice,
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
import type { BoardId } from 'features/gallery/store/types';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
@@ -35,18 +37,19 @@ const BoardContextMenu = ({
|
||||
}: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const selector = useMemo(
|
||||
const autoAssignBoardOnClick = useAppSelector(
|
||||
(s) => s.gallery.autoAssignBoardOnClick
|
||||
);
|
||||
const selectIsSelectedForAutoAdd = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(stateSelector, ({ gallery }) => {
|
||||
const isAutoAdd = gallery.autoAddBoardId === board_id;
|
||||
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick;
|
||||
return { isAutoAdd, autoAssignBoardOnClick };
|
||||
}),
|
||||
[board_id]
|
||||
createSelector(
|
||||
selectGallerySlice,
|
||||
(gallery) => board && board.board_id === gallery.autoAddBoardId
|
||||
),
|
||||
[board]
|
||||
);
|
||||
|
||||
const { isAutoAdd, autoAssignBoardOnClick } = useAppSelector(selector);
|
||||
const isSelectedForAutoAdd = useAppSelector(selectIsSelectedForAutoAdd);
|
||||
const boardName = useBoardName(board_id);
|
||||
const isBulkDownloadEnabled =
|
||||
useFeatureStatus('bulkDownload').isFeatureEnabled;
|
||||
@@ -97,7 +100,7 @@ const BoardContextMenu = ({
|
||||
<InvMenuGroup title={boardName}>
|
||||
<InvMenuItem
|
||||
icon={<FaPlus />}
|
||||
isDisabled={isAutoAdd || autoAssignBoardOnClick}
|
||||
isDisabled={isSelectedForAutoAdd || autoAssignBoardOnClick}
|
||||
onClick={handleSetAutoAdd}
|
||||
>
|
||||
{t('boards.menuItemAutoAdd')}
|
||||
@@ -125,8 +128,8 @@ const BoardContextMenu = ({
|
||||
boardName,
|
||||
handleBulkDownload,
|
||||
handleSetAutoAdd,
|
||||
isAutoAdd,
|
||||
isBulkDownloadEnabled,
|
||||
isSelectedForAutoAdd,
|
||||
setBoardToDelete,
|
||||
skipEvent,
|
||||
t,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Collapse, Flex, Grid, GridItem } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
||||
import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
|
||||
@@ -20,18 +18,14 @@ const overlayScrollbarsStyles: CSSProperties = {
|
||||
width: '100%',
|
||||
};
|
||||
|
||||
const selector = createMemoizedSelector([stateSelector], ({ gallery }) => {
|
||||
const { selectedBoardId, boardSearchText } = gallery;
|
||||
return { selectedBoardId, boardSearchText };
|
||||
});
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
};
|
||||
|
||||
const BoardsList = (props: Props) => {
|
||||
const { isOpen } = props;
|
||||
const { selectedBoardId, boardSearchText } = useAppSelector(selector);
|
||||
const selectedBoardId = useAppSelector((s) => s.gallery.selectedBoardId);
|
||||
const boardSearchText = useAppSelector((s) => s.gallery.boardSearchText);
|
||||
const { data: boards } = useListAllBoardsQuery();
|
||||
const filteredBoards = boardSearchText
|
||||
? boards?.filter((board) =>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { CloseIcon } from '@chakra-ui/icons';
|
||||
import { Input, InputGroup, InputRightElement } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
||||
import { boardSearchTextChanged } from 'features/gallery/store/gallerySlice';
|
||||
@@ -9,14 +7,9 @@ import type { ChangeEvent, KeyboardEvent } from 'react';
|
||||
import { memo, useCallback, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createMemoizedSelector([stateSelector], ({ gallery }) => {
|
||||
const { boardSearchText } = gallery;
|
||||
return { boardSearchText };
|
||||
});
|
||||
|
||||
const BoardsSearch = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { boardSearchText } = useAppSelector(selector);
|
||||
const boardSearchText = useAppSelector((s) => s.gallery.boardSearchText);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
@@ -8,9 +8,8 @@ import {
|
||||
Icon,
|
||||
Image,
|
||||
} from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIDroppable from 'common/components/IAIDroppable';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
@@ -22,6 +21,7 @@ import BoardContextMenu from 'features/gallery/components/Boards/BoardContextMen
|
||||
import {
|
||||
autoAddBoardIdChanged,
|
||||
boardIdSelected,
|
||||
selectGallerySlice,
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -54,22 +54,19 @@ const GalleryBoard = ({
|
||||
setBoardToDelete,
|
||||
}: GalleryBoardProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const selector = useMemo(
|
||||
const autoAssignBoardOnClick = useAppSelector(
|
||||
(s) => s.gallery.autoAssignBoardOnClick
|
||||
);
|
||||
const selectIsSelectedForAutoAdd = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(stateSelector, ({ gallery }) => {
|
||||
const isSelectedForAutoAdd = board.board_id === gallery.autoAddBoardId;
|
||||
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick;
|
||||
|
||||
return {
|
||||
isSelectedForAutoAdd,
|
||||
autoAssignBoardOnClick,
|
||||
};
|
||||
}),
|
||||
createSelector(
|
||||
selectGallerySlice,
|
||||
(gallery) => board.board_id === gallery.autoAddBoardId
|
||||
),
|
||||
[board.board_id]
|
||||
);
|
||||
|
||||
const { isSelectedForAutoAdd, autoAssignBoardOnClick } =
|
||||
useAppSelector(selector);
|
||||
const isSelectedForAutoAdd = useAppSelector(selectIsSelectedForAutoAdd);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const handleMouseOver = useCallback(() => {
|
||||
setIsHovered(true);
|
||||
@@ -221,7 +218,7 @@ const GalleryBoard = ({
|
||||
w="full"
|
||||
maxW="full"
|
||||
borderBottomRadius="base"
|
||||
bg={isSelected ? 'blue.500' : 'base.600'}
|
||||
bg={isSelected ? 'invokeBlue.500' : 'base.600'}
|
||||
color={isSelected ? 'base.50' : 'base.100'}
|
||||
lineHeight="short"
|
||||
fontSize="xs"
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Box, Flex, Image } from '@chakra-ui/react';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import InvokeAILogoImage from 'assets/images/logo.png';
|
||||
import IAIDroppable from 'common/components/IAIDroppable';
|
||||
@@ -26,14 +24,12 @@ interface Props {
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
const selector = createMemoizedSelector(stateSelector, ({ gallery }) => {
|
||||
const { autoAddBoardId, autoAssignBoardOnClick } = gallery;
|
||||
return { autoAddBoardId, autoAssignBoardOnClick };
|
||||
});
|
||||
|
||||
const NoBoardBoard = memo(({ isSelected }: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { autoAddBoardId, autoAssignBoardOnClick } = useAppSelector(selector);
|
||||
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
|
||||
const autoAssignBoardOnClick = useAppSelector(
|
||||
(s) => s.gallery.autoAssignBoardOnClick
|
||||
);
|
||||
const boardName = useBoardName('none');
|
||||
const handleSelectBoard = useCallback(() => {
|
||||
dispatch(boardIdSelected({ boardId: 'none' }));
|
||||
@@ -128,7 +124,7 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
|
||||
w="full"
|
||||
maxW="full"
|
||||
borderBottomRadius="base"
|
||||
bg={isSelected ? 'blue.500' : 'base.600'}
|
||||
bg={isSelected ? 'invokeBlue.500' : 'base.600'}
|
||||
color={isSelected ? 'base.50' : 'base.100'}
|
||||
lineHeight="short"
|
||||
fontSize="xs"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Flex, Skeleton } from '@chakra-ui/react';
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
InvAlertDialog,
|
||||
@@ -13,9 +12,13 @@ import {
|
||||
} from 'common/components/InvAlertDialog/wrapper';
|
||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||
import { selectControlAdaptersSlice } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import ImageUsageMessage from 'features/deleteImageModal/components/ImageUsageMessage';
|
||||
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||
import type { ImageUsage } from 'features/deleteImageModal/store/types';
|
||||
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
||||
import { some } from 'lodash-es';
|
||||
import { memo, useCallback, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -35,26 +38,35 @@ const DeleteBoardModal = (props: Props) => {
|
||||
const { boardToDelete, setBoardToDelete } = props;
|
||||
const { t } = useTranslation();
|
||||
const canRestoreDeletedImagesFromBin = useAppSelector(
|
||||
(state) => state.config.canRestoreDeletedImagesFromBin
|
||||
(s) => s.config.canRestoreDeletedImagesFromBin
|
||||
);
|
||||
const { currentData: boardImageNames, isFetching: isFetchingBoardNames } =
|
||||
useListAllImageNamesForBoardQuery(boardToDelete?.board_id ?? skipToken);
|
||||
|
||||
const selectImageUsageSummary = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector([stateSelector], (state) => {
|
||||
const allImageUsage = (boardImageNames ?? []).map((imageName) =>
|
||||
getImageUsage(state, imageName)
|
||||
);
|
||||
createMemoizedSelector(
|
||||
[
|
||||
selectGenerationSlice,
|
||||
selectCanvasSlice,
|
||||
selectNodesSlice,
|
||||
selectControlAdaptersSlice,
|
||||
],
|
||||
(generation, canvas, nodes, controlAdapters) => {
|
||||
const allImageUsage = (boardImageNames ?? []).map((imageName) =>
|
||||
getImageUsage(generation, canvas, nodes, controlAdapters, imageName)
|
||||
);
|
||||
|
||||
const imageUsageSummary: ImageUsage = {
|
||||
isInitialImage: some(allImageUsage, (i) => i.isInitialImage),
|
||||
isCanvasImage: some(allImageUsage, (i) => i.isCanvasImage),
|
||||
isNodesImage: some(allImageUsage, (i) => i.isNodesImage),
|
||||
isControlImage: some(allImageUsage, (i) => i.isControlImage),
|
||||
};
|
||||
return { imageUsageSummary };
|
||||
}),
|
||||
const imageUsageSummary: ImageUsage = {
|
||||
isInitialImage: some(allImageUsage, (i) => i.isInitialImage),
|
||||
isCanvasImage: some(allImageUsage, (i) => i.isCanvasImage),
|
||||
isNodesImage: some(allImageUsage, (i) => i.isNodesImage),
|
||||
isControlImage: some(allImageUsage, (i) => i.isControlImage),
|
||||
};
|
||||
|
||||
return imageUsageSummary;
|
||||
}
|
||||
),
|
||||
[boardImageNames]
|
||||
);
|
||||
|
||||
@@ -64,7 +76,7 @@ const DeleteBoardModal = (props: Props) => {
|
||||
const [deleteBoardAndImages, { isLoading: isDeleteBoardAndImagesLoading }] =
|
||||
useDeleteBoardAndImagesMutation();
|
||||
|
||||
const { imageUsageSummary } = useAppSelector(selectImageUsageSummary);
|
||||
const imageUsageSummary = useAppSelector(selectImageUsageSummary);
|
||||
|
||||
const handleDeleteBoardOnly = useCallback(() => {
|
||||
if (!boardToDelete) {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useAppToaster } from 'app/components/Toaster';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { upscaleRequested } from 'app/store/middleware/listenerMiddleware/listeners/upscaleRequested';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup';
|
||||
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
||||
@@ -13,12 +12,14 @@ import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteIm
|
||||
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
|
||||
import { sentImageToImg2Img } from 'features/gallery/store/actions';
|
||||
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
|
||||
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
||||
import ParamUpscalePopover from 'features/parameters/components/Upscale/ParamUpscaleSettings';
|
||||
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||
import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { selectSystemSlice } from 'features/system/store/systemSlice';
|
||||
import {
|
||||
setShouldShowImageDetails,
|
||||
setShouldShowProgressInViewer,
|
||||
@@ -39,45 +40,29 @@ import { FaCircleNodes, FaEllipsis } from 'react-icons/fa6';
|
||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
|
||||
|
||||
const currentImageButtonsSelector = createMemoizedSelector(
|
||||
[stateSelector, activeTabNameSelector],
|
||||
({ gallery, system, ui, config }, activeTabName) => {
|
||||
const { isConnected, shouldConfirmOnDelete, denoiseProgress } = system;
|
||||
|
||||
const {
|
||||
shouldShowImageDetails,
|
||||
shouldHidePreview,
|
||||
shouldShowProgressInViewer,
|
||||
} = ui;
|
||||
|
||||
const { shouldFetchMetadataFromApi } = config;
|
||||
|
||||
const lastSelectedImage = gallery.selection[gallery.selection.length - 1];
|
||||
|
||||
return {
|
||||
shouldConfirmOnDelete,
|
||||
isConnected,
|
||||
shouldDisableToolbarButtons:
|
||||
Boolean(denoiseProgress?.progress_image) || !lastSelectedImage,
|
||||
shouldShowImageDetails,
|
||||
activeTabName,
|
||||
shouldHidePreview,
|
||||
shouldShowProgressInViewer,
|
||||
lastSelectedImage,
|
||||
shouldFetchMetadataFromApi,
|
||||
};
|
||||
const selectShouldDisableToolbarButtons = createSelector(
|
||||
selectSystemSlice,
|
||||
selectGallerySlice,
|
||||
selectLastSelectedImage,
|
||||
(system, gallery, lastSelectedImage) => {
|
||||
const hasProgressImage = Boolean(system.denoiseProgress?.progress_image);
|
||||
return hasProgressImage || !lastSelectedImage;
|
||||
}
|
||||
);
|
||||
|
||||
const CurrentImageButtons = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
isConnected,
|
||||
shouldDisableToolbarButtons,
|
||||
shouldShowImageDetails,
|
||||
lastSelectedImage,
|
||||
shouldShowProgressInViewer,
|
||||
} = useAppSelector(currentImageButtonsSelector);
|
||||
const isConnected = useAppSelector((s) => s.system.isConnected);
|
||||
const shouldShowImageDetails = useAppSelector(
|
||||
(s) => s.ui.shouldShowImageDetails
|
||||
);
|
||||
const shouldShowProgressInViewer = useAppSelector(
|
||||
(s) => s.ui.shouldShowProgressInViewer
|
||||
);
|
||||
const lastSelectedImage = useAppSelector(selectLastSelectedImage);
|
||||
const shouldDisableToolbarButtons = useAppSelector(
|
||||
selectShouldDisableToolbarButtons
|
||||
);
|
||||
|
||||
const isUpscalingEnabled = useFeatureStatus('upscaling').isFeatureEnabled;
|
||||
const isQueueMutationInProgress = useIsQueueMutationInProgress();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
@@ -23,32 +22,22 @@ import { useTranslation } from 'react-i18next';
|
||||
import { FaImage } from 'react-icons/fa';
|
||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||
|
||||
export const imagesSelector = createMemoizedSelector(
|
||||
[stateSelector, selectLastSelectedImage],
|
||||
({ ui, system }, lastSelectedImage) => {
|
||||
const {
|
||||
shouldShowImageDetails,
|
||||
shouldHidePreview,
|
||||
shouldShowProgressInViewer,
|
||||
} = ui;
|
||||
const { denoiseProgress } = system;
|
||||
return {
|
||||
shouldShowImageDetails,
|
||||
shouldHidePreview,
|
||||
imageName: lastSelectedImage?.image_name,
|
||||
hasDenoiseProgress: Boolean(denoiseProgress),
|
||||
shouldShowProgressInViewer,
|
||||
};
|
||||
}
|
||||
const selectLastSelectedImageName = createSelector(
|
||||
selectLastSelectedImage,
|
||||
(lastSelectedImage) => lastSelectedImage?.image_name
|
||||
);
|
||||
|
||||
const CurrentImagePreview = () => {
|
||||
const {
|
||||
shouldShowImageDetails,
|
||||
imageName,
|
||||
hasDenoiseProgress,
|
||||
shouldShowProgressInViewer,
|
||||
} = useAppSelector(imagesSelector);
|
||||
const shouldShowImageDetails = useAppSelector(
|
||||
(s) => s.ui.shouldShowImageDetails
|
||||
);
|
||||
const imageName = useAppSelector(selectLastSelectedImageName);
|
||||
const hasDenoiseProgress = useAppSelector((s) =>
|
||||
Boolean(s.system.denoiseProgress)
|
||||
);
|
||||
const shouldShowProgressInViewer = useAppSelector(
|
||||
(s) => s.ui.shouldShowProgressInViewer
|
||||
);
|
||||
|
||||
const {
|
||||
handlePrevImage,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user