From 35dd58e273c64059ce0d708db36e85c8670ac520 Mon Sep 17 00:00:00 2001 From: Saurav Maheshkar Date: Sat, 29 Jul 2023 12:59:56 +0530 Subject: [PATCH 01/14] chore: move PR template to `.github/` dir --- pull_request_template.md => .github/pull_request_template.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pull_request_template.md => .github/pull_request_template.md (100%) diff --git a/pull_request_template.md b/.github/pull_request_template.md similarity index 100% rename from pull_request_template.md rename to .github/pull_request_template.md From 7cd8b2f20712e8f28ebdf8e888603ef366d001aa Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Mon, 31 Jul 2023 21:15:44 -0400 Subject: [PATCH 02/14] Refactor root detection code --- invokeai/app/services/config.py | 13 ++++--------- invokeai/frontend/install/model_install.py | 2 ++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/invokeai/app/services/config.py b/invokeai/app/services/config.py index a9a4a64f75..c75c0602b1 100644 --- a/invokeai/app/services/config.py +++ b/invokeai/app/services/config.py @@ -274,7 +274,7 @@ class InvokeAISettings(BaseSettings): @classmethod def _excluded(self) -> List[str]: # internal fields that shouldn't be exposed as command line options - return ["type", "initconf", "cached_root"] + return ["type", "initconf"] @classmethod def _excluded_from_yaml(self) -> List[str]: @@ -290,7 +290,6 @@ class InvokeAISettings(BaseSettings): "restore", "root", "nsfw_checker", - "cached_root", ] class Config: @@ -357,6 +356,7 @@ def _find_root() -> Path: venv = Path(os.environ.get("VIRTUAL_ENV") or ".") if os.environ.get("INVOKEAI_ROOT"): root = Path(os.environ.get("INVOKEAI_ROOT")).resolve() + os.environ["INVOKEAI_ROOT"] = str(root) # absolutize it to protect against code doing a cwd() elif any([(venv.parent / x).exists() for x in [INIT_FILE, LEGACY_INIT_FILE]]): root = (venv.parent).resolve() else: @@ -424,7 +424,6 @@ class InvokeAIAppConfig(InvokeAISettings): log_level : Literal[tuple(["debug","info","warning","error","critical"])] = Field(default="info", description="Emit logging messages at this level or higher", category="Logging") version : bool = Field(default=False, description="Show InvokeAI version and exit", category="Other") - cached_root : Path = Field(default=None, description="internal use only", category="DEPRECATED") # fmt: on def parse_args(self, argv: List[str] = None, conf: DictConfig = None, clobber=False): @@ -472,15 +471,11 @@ class InvokeAIAppConfig(InvokeAISettings): """ Path to the runtime root directory """ - # we cache value of root to protect against it being '.' and the cwd changing - if self.cached_root: - root = self.cached_root - elif self.root: + if self.root: root = Path(self.root).expanduser().absolute() else: root = self.find_root() - self.cached_root = root - return self.cached_root + return root @property def root_dir(self) -> Path: diff --git a/invokeai/frontend/install/model_install.py b/invokeai/frontend/install/model_install.py index 0633553a3d..3eb6425f77 100644 --- a/invokeai/frontend/install/model_install.py +++ b/invokeai/frontend/install/model_install.py @@ -767,7 +767,9 @@ def main(): invoke_args.extend(["--root", opt.root]) if opt.full_precision: invoke_args.extend(["--precision", "float32"]) + print(f"DEBUG: {invoke_args}") config.parse_args(invoke_args) + print(f"DEBUG: {config.root} {config.root_path}") logger = InvokeAILogger().getLogger(config=config) if not config.model_conf_path.exists(): From 72ebe2ce686a6c1188f17fde5ac9225cda9f0d5b Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Mon, 31 Jul 2023 22:27:07 -0400 Subject: [PATCH 03/14] refactor root directory detection to be cleaner --- invokeai/app/services/config.py | 4 ++-- tests/test_config.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/invokeai/app/services/config.py b/invokeai/app/services/config.py index c75c0602b1..434ba47fc1 100644 --- a/invokeai/app/services/config.py +++ b/invokeai/app/services/config.py @@ -356,7 +356,6 @@ def _find_root() -> Path: venv = Path(os.environ.get("VIRTUAL_ENV") or ".") if os.environ.get("INVOKEAI_ROOT"): root = Path(os.environ.get("INVOKEAI_ROOT")).resolve() - os.environ["INVOKEAI_ROOT"] = str(root) # absolutize it to protect against code doing a cwd() elif any([(venv.parent / x).exists() for x in [INIT_FILE, LEGACY_INIT_FILE]]): root = (venv.parent).resolve() else: @@ -403,7 +402,7 @@ class InvokeAIAppConfig(InvokeAISettings): xformers_enabled : bool = Field(default=True, description="Enable/disable memory-efficient attention", category='Memory/Performance') tiled_decode : bool = Field(default=False, description="Whether to enable tiled VAE decode (reduces memory consumption with some performance penalty)", category='Memory/Performance') - root : Path = Field(default=_find_root(), description='InvokeAI runtime root directory', category='Paths') + root : Path = Field(default=None, description='InvokeAI runtime root directory', category='Paths') autoimport_dir : Path = Field(default='autoimport', description='Path to a directory of models files to be imported on startup.', category='Paths') lora_dir : Path = Field(default=None, description='Path to a directory of LoRA/LyCORIS models to be imported on startup.', category='Paths') embedding_dir : Path = Field(default=None, description='Path to a directory of Textual Inversion embeddings to be imported on startup.', category='Paths') @@ -475,6 +474,7 @@ class InvokeAIAppConfig(InvokeAISettings): root = Path(self.root).expanduser().absolute() else: root = self.find_root() + self.root = root # insulate ourselves from relative paths that may change return root @property diff --git a/tests/test_config.py b/tests/test_config.py index 5d3dc46aa4..a84af80eb9 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -84,6 +84,20 @@ def test_env_override(): assert conf.max_cache_size == 20 +def test_root_resists_cwd(): + previous = os.environ["INVOKEAI_ROOT"] + cwd = Path(os.getcwd()).resolve() + + os.environ["INVOKEAI_ROOT"] = "." + conf = InvokeAIAppConfig.get_config() + conf.parse_args([]) + assert conf.root_path == cwd + + os.chdir(previous) + assert conf.root_path == cwd + os.environ["INVOKEAI_ROOT"] = previous + + def test_type_coercion(): conf = InvokeAIAppConfig().get_config() conf.parse_args(argv=["--root=/tmp/foobar"]) From 55d3f04476c3b8cc529ae49460e974ecb8aee454 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Mon, 31 Jul 2023 22:36:11 -0400 Subject: [PATCH 04/14] additional refactoring --- invokeai/app/services/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/invokeai/app/services/config.py b/invokeai/app/services/config.py index 434ba47fc1..c119f2f74c 100644 --- a/invokeai/app/services/config.py +++ b/invokeai/app/services/config.py @@ -355,7 +355,7 @@ class InvokeAISettings(BaseSettings): def _find_root() -> Path: venv = Path(os.environ.get("VIRTUAL_ENV") or ".") if os.environ.get("INVOKEAI_ROOT"): - root = Path(os.environ.get("INVOKEAI_ROOT")).resolve() + root = Path(os.environ["INVOKEAI_ROOT"]) elif any([(venv.parent / x).exists() for x in [INIT_FILE, LEGACY_INIT_FILE]]): root = (venv.parent).resolve() else: @@ -473,7 +473,7 @@ class InvokeAIAppConfig(InvokeAISettings): if self.root: root = Path(self.root).expanduser().absolute() else: - root = self.find_root() + root = self.find_root().expanduser().absolute() self.root = root # insulate ourselves from relative paths that may change return root From df53b6204805da502709b870c6bdc3453c0a6c6d Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Mon, 31 Jul 2023 22:39:11 -0400 Subject: [PATCH 05/14] get rid of dangling debug statements --- invokeai/frontend/install/model_install.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/invokeai/frontend/install/model_install.py b/invokeai/frontend/install/model_install.py index 3eb6425f77..0633553a3d 100644 --- a/invokeai/frontend/install/model_install.py +++ b/invokeai/frontend/install/model_install.py @@ -767,9 +767,7 @@ def main(): invoke_args.extend(["--root", opt.root]) if opt.full_precision: invoke_args.extend(["--precision", "float32"]) - print(f"DEBUG: {invoke_args}") config.parse_args(invoke_args) - print(f"DEBUG: {config.root} {config.root_path}") logger = InvokeAILogger().getLogger(config=config) if not config.model_conf_path.exists(): From 437f45a97faa55681fdc1fe82736cf3d74063c08 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Tue, 1 Aug 2023 00:41:35 -0400 Subject: [PATCH 06/14] do not depend on existence of /tmp directory --- tests/test_config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index a84af80eb9..721edce487 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -93,9 +93,10 @@ def test_root_resists_cwd(): conf.parse_args([]) assert conf.root_path == cwd - os.chdir(previous) + os.chdir("..") assert conf.root_path == cwd os.environ["INVOKEAI_ROOT"] = previous + os.chdir(cwd) def test_type_coercion(): From 0d125bf3e43567f88adeec5a4f6494e7306912b6 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 1 Aug 2023 23:59:32 +1000 Subject: [PATCH 07/14] chore: delete nonfunctional `shell.nix` This was for v2.3 and is very broken. See `flake.nix`, thanks to @zopieux --- shell.nix | 162 ------------------------------------------------------ 1 file changed, 162 deletions(-) delete mode 100644 shell.nix diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 3de8c91b38..0000000000 --- a/shell.nix +++ /dev/null @@ -1,162 +0,0 @@ -{ pkgs ? import {} - , lib ? pkgs.lib - , stdenv ? pkgs.stdenv - , fetchurl ? pkgs.fetchurl - , runCommand ? pkgs.runCommand - , makeWrapper ? pkgs.makeWrapper - , mkShell ? pkgs.mkShell - , buildFHSUserEnv ? pkgs.buildFHSUserEnv - , frameworks ? pkgs.darwin.apple_sdk.frameworks -}: - -# Setup InvokeAI environment using nix -# Simple usage: -# nix-shell -# python3 scripts/preload_models.py -# python3 scripts/invoke.py -h - -let - conda-shell = { url, sha256, installPath, packages, shellHook }: - let - src = fetchurl { inherit url sha256; }; - libPath = lib.makeLibraryPath ([] ++ lib.optionals (stdenv.isLinux) [ pkgs.zlib ]); - condaArch = if stdenv.system == "aarch64-darwin" then "osx-arm64" else ""; - installer = - if stdenv.isDarwin then - runCommand "conda-install" { - nativeBuildInputs = [ makeWrapper ]; - } '' - mkdir -p $out/bin - cp ${src} $out/bin/miniconda-installer.sh - chmod +x $out/bin/miniconda-installer.sh - makeWrapper \ - $out/bin/miniconda-installer.sh \ - $out/bin/conda-install \ - --add-flags "-p ${installPath}" \ - --add-flags "-b" - '' - else if stdenv.isLinux then - runCommand "conda-install" { - nativeBuildInputs = [ makeWrapper ]; - buildInputs = [ pkgs.zlib ]; - } - # on line 10, we have 'unset LD_LIBRARY_PATH' - # we have to comment it out however in a way that the number of bytes in the - # file does not change. So we replace the 'u' in the line with a '#' - # The reason is that the binary payload is encoded as number - # of bytes from the top of the installer script - # and unsetting the library path prevents the zlib library from being discovered - '' - mkdir -p $out/bin - sed 's/unset LD_LIBRARY_PATH/#nset LD_LIBRARY_PATH/' ${src} > $out/bin/miniconda-installer.sh - chmod +x $out/bin/miniconda-installer.sh - makeWrapper \ - $out/bin/miniconda-installer.sh \ - $out/bin/conda-install \ - --add-flags "-p ${installPath}" \ - --add-flags "-b" \ - --prefix "LD_LIBRARY_PATH" : "${libPath}" - '' - else {}; - - hook = '' - export CONDA_SUBDIR=${condaArch} - '' + shellHook; - - fhs = buildFHSUserEnv { - name = "conda-shell"; - targetPkgs = pkgs: [ stdenv.cc pkgs.git installer ] ++ packages; - profile = hook; - runScript = "bash"; - }; - - shell = mkShell { - shellHook = if stdenv.isDarwin then hook else "conda-shell; exit"; - packages = if stdenv.isDarwin then [ pkgs.git installer ] ++ packages else [ fhs ]; - }; - in shell; - - packages = with pkgs; [ - cmake - protobuf - libiconv - rustc - cargo - rustPlatform.bindgenHook - ]; - - env = { - aarch64-darwin = { - envFile = "environment-mac.yml"; - condaPath = (builtins.toString ./.) + "/.conda"; - ptrSize = "8"; - }; - x86_64-linux = { - envFile = "environment.yml"; - condaPath = (builtins.toString ./.) + "/.conda"; - ptrSize = "8"; - }; - }; - - envFile = env.${stdenv.system}.envFile; - installPath = env.${stdenv.system}.condaPath; - ptrSize = env.${stdenv.system}.ptrSize; - shellHook = '' - conda-install - - # tmpdir is too small in nix - export TMPDIR="${installPath}/tmp" - - # Add conda to PATH - export PATH="${installPath}/bin:$PATH" - - # Allows `conda activate` to work properly - source ${installPath}/etc/profile.d/conda.sh - - # Paths for gcc if compiling some C sources with pip - export NIX_CFLAGS_COMPILE="-I${installPath}/include -I$TMPDIR/include" - export NIX_CFLAGS_LINK="-L${installPath}/lib $BINDGEN_EXTRA_CLANG_ARGS" - - export PIP_EXISTS_ACTION=w - - # rust-onig fails (think it writes config.h to wrong location) - mkdir -p "$TMPDIR/include" - cat <<'EOF' > "$TMPDIR/include/config.h" - #define HAVE_PROTOTYPES 1 - #define STDC_HEADERS 1 - #define HAVE_STRING_H 1 - #define HAVE_STDARG_H 1 - #define HAVE_STDLIB_H 1 - #define HAVE_LIMITS_H 1 - #define HAVE_INTTYPES_H 1 - #define SIZEOF_INT 4 - #define SIZEOF_SHORT 2 - #define SIZEOF_LONG ${ptrSize} - #define SIZEOF_VOIDP ${ptrSize} - #define SIZEOF_LONG_LONG 8 - EOF - - conda env create -f "${envFile}" || conda env update --prune -f "${envFile}" - conda activate invokeai - ''; - - version = "4.12.0"; - conda = { - aarch64-darwin = { - shell = conda-shell { - inherit shellHook installPath; - url = "https://repo.anaconda.com/miniconda/Miniconda3-py39_${version}-MacOSX-arm64.sh"; - sha256 = "4bd112168cc33f8a4a60d3ef7e72b52a85972d588cd065be803eb21d73b625ef"; - packages = [ frameworks.Security ] ++ packages; - }; - }; - x86_64-linux = { - shell = conda-shell { - inherit shellHook installPath; - url = "https://repo.continuum.io/miniconda/Miniconda3-py39_${version}-Linux-x86_64.sh"; - sha256 = "78f39f9bae971ec1ae7969f0516017f2413f17796670f7040725dd83fcff5689"; - packages = with pkgs; [ libGL glib ] ++ packages; - }; - }; - }; -in conda.${stdenv.system}.shell From 0ba8a0ea6c4f874db22d91b3192f8c084f8a6fd2 Mon Sep 17 00:00:00 2001 From: Kevin Brack Date: Sun, 30 Jul 2023 13:05:29 -0500 Subject: [PATCH 08/14] Board assignment changing on click --- .../components/GallerySettingsPopover.tsx | 19 ++++++++++++++++--- .../features/gallery/store/gallerySlice.ts | 6 ++++++ .../web/src/features/gallery/store/types.ts | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx b/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx index 21a580d9a9..04cc98edb7 100644 --- a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx @@ -8,6 +8,7 @@ import IAIPopover from 'common/components/IAIPopover'; import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox'; import IAISlider from 'common/components/IAISlider'; import { + autoAssignBoardOnClickChanged, setGalleryImageMinimumWidth, shouldAutoSwitchChanged, } from 'features/gallery/store/gallerySlice'; @@ -19,11 +20,16 @@ import BoardAutoAddSelect from './Boards/BoardAutoAddSelect'; const selector = createSelector( [stateSelector], (state) => { - const { galleryImageMinimumWidth, shouldAutoSwitch } = state.gallery; + const { + galleryImageMinimumWidth, + shouldAutoSwitch, + autoAssignBoardOnClick, + } = state.gallery; return { galleryImageMinimumWidth, shouldAutoSwitch, + autoAssignBoardOnClick, }; }, defaultSelectorOptions @@ -33,7 +39,7 @@ const GallerySettingsPopover = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const { galleryImageMinimumWidth, shouldAutoSwitch } = + const { galleryImageMinimumWidth, shouldAutoSwitch, autoAssignBoardOnClick } = useAppSelector(selector); const handleChangeGalleryImageMinimumWidth = (v: number) => { @@ -69,7 +75,14 @@ const GallerySettingsPopover = () => { dispatch(shouldAutoSwitchChanged(e.target.checked)) } /> - + ) => + dispatch(autoAssignBoardOnClickChanged(e.target.checked)) + } + /> + {!autoAssignBoardOnClick && } ); diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index 5eabe5de26..851e1b6c3b 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -8,6 +8,7 @@ export const initialGalleryState: GalleryState = { selection: [], shouldAutoSwitch: true, autoAddBoardId: undefined, + autoAssignBoardOnClick: true, galleryImageMinimumWidth: 96, selectedBoardId: undefined, galleryView: 'images', @@ -66,9 +67,13 @@ export const gallerySlice = createSlice({ setGalleryImageMinimumWidth: (state, action: PayloadAction) => { state.galleryImageMinimumWidth = action.payload; }, + autoAssignBoardOnClickChanged: (state, action: PayloadAction) => { + state.autoAssignBoardOnClick = action.payload; + }, boardIdSelected: (state, action: PayloadAction) => { state.selectedBoardId = action.payload; state.galleryView = 'images'; + state.autoAssignBoardOnClick && (state.autoAddBoardId = action.payload); }, isBatchEnabledChanged: (state, action: PayloadAction) => { state.isBatchEnabled = action.payload; @@ -140,6 +145,7 @@ export const { imageSelectionToggled, imageSelected, shouldAutoSwitchChanged, + autoAssignBoardOnClickChanged, setGalleryImageMinimumWidth, boardIdSelected, isBatchEnabledChanged, diff --git a/invokeai/frontend/web/src/features/gallery/store/types.ts b/invokeai/frontend/web/src/features/gallery/store/types.ts index d19a6fded3..298b792362 100644 --- a/invokeai/frontend/web/src/features/gallery/store/types.ts +++ b/invokeai/frontend/web/src/features/gallery/store/types.ts @@ -18,6 +18,7 @@ export type GalleryState = { selection: string[]; shouldAutoSwitch: boolean; autoAddBoardId: string | undefined; + autoAssignBoardOnClick: boolean; galleryImageMinimumWidth: number; selectedBoardId: BoardId; galleryView: GalleryView; From 450e95de592ac623bce8acca2ebeb60bbeb1a24a Mon Sep 17 00:00:00 2001 From: Kevin Brack Date: Sun, 30 Jul 2023 18:35:18 -0500 Subject: [PATCH 09/14] auto change board waiting for isReady --- .../Boards/BoardsList/GalleryBoard.tsx | 19 +++++++++++++++---- .../Boards/BoardsList/NoBoardBoard.tsx | 18 +++++++++++++----- .../features/gallery/store/gallerySlice.ts | 1 - 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx index 67c45c131b..a4124e2393 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx @@ -16,7 +16,10 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAIDroppable from 'common/components/IAIDroppable'; import SelectionOverlay from 'common/components/SelectionOverlay'; -import { boardIdSelected } from 'features/gallery/store/gallerySlice'; +import { + autoAddBoardIdChanged, + boardIdSelected, +} from 'features/gallery/store/gallerySlice'; import { memo, useCallback, useMemo, useState } from 'react'; import { FaUser } from 'react-icons/fa'; import { useUpdateBoardMutation } from 'services/api/endpoints/boards'; @@ -24,6 +27,7 @@ import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { BoardDTO } from 'services/api/types'; import AutoAddIcon from '../AutoAddIcon'; import BoardContextMenu from '../BoardContextMenu'; +import { useIsReadyToInvoke } from 'common/hooks/useIsReadyToInvoke'; interface GalleryBoardProps { board: BoardDTO; @@ -41,15 +45,17 @@ const GalleryBoard = memo( ({ gallery }) => { const isSelectedForAutoAdd = board.board_id === gallery.autoAddBoardId; + const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick; - return { isSelectedForAutoAdd }; + return { isSelectedForAutoAdd, autoAssignBoardOnClick }; }, defaultSelectorOptions ), [board.board_id] ); - const { isSelectedForAutoAdd } = useAppSelector(selector); + const { isSelectedForAutoAdd, autoAssignBoardOnClick } = + useAppSelector(selector); const [isHovered, setIsHovered] = useState(false); const handleMouseOver = useCallback(() => { setIsHovered(true); @@ -64,9 +70,14 @@ const GalleryBoard = memo( const { board_name, board_id } = board; const [localBoardName, setLocalBoardName] = useState(board_name); + const isReady = useIsReadyToInvoke(); + const handleSelectBoard = useCallback(() => { dispatch(boardIdSelected(board_id)); - }, [board_id, dispatch]); + if (autoAssignBoardOnClick && isReady) { + dispatch(autoAddBoardIdChanged(board_id)); + } + }, [board_id, autoAssignBoardOnClick, isReady, dispatch]); const [updateBoard, { isLoading: isUpdateBoardLoading }] = useUpdateBoardMutation(); diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx index ee1d8f6bea..3963fc04d9 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx @@ -7,11 +7,15 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import InvokeAILogoImage from 'assets/images/logo.png'; import IAIDroppable from 'common/components/IAIDroppable'; import SelectionOverlay from 'common/components/SelectionOverlay'; -import { boardIdSelected } from 'features/gallery/store/gallerySlice'; +import { + boardIdSelected, + autoAddBoardIdChanged, +} from 'features/gallery/store/gallerySlice'; import { memo, useCallback, useMemo, useState } from 'react'; import { useBoardName } from 'services/api/hooks/useBoardName'; import AutoAddIcon from '../AutoAddIcon'; import BoardContextMenu from '../BoardContextMenu'; +import { useIsReadyToInvoke } from 'common/hooks/useIsReadyToInvoke'; interface Props { isSelected: boolean; } @@ -19,19 +23,23 @@ interface Props { const selector = createSelector( stateSelector, ({ gallery }) => { - const { autoAddBoardId } = gallery; - return { autoAddBoardId }; + const { autoAddBoardId, autoAssignBoardOnClick } = gallery; + return { autoAddBoardId, autoAssignBoardOnClick }; }, defaultSelectorOptions ); const NoBoardBoard = memo(({ isSelected }: Props) => { const dispatch = useAppDispatch(); - const { autoAddBoardId } = useAppSelector(selector); + const { autoAddBoardId, autoAssignBoardOnClick } = useAppSelector(selector); const boardName = useBoardName(undefined); + const isReady = useIsReadyToInvoke(); const handleSelectBoard = useCallback(() => { dispatch(boardIdSelected(undefined)); - }, [dispatch]); + if (autoAssignBoardOnClick && isReady) { + dispatch(autoAddBoardIdChanged(undefined)); + } + }, [dispatch, autoAssignBoardOnClick, isReady]); const [isHovered, setIsHovered] = useState(false); const handleMouseOver = useCallback(() => { setIsHovered(true); diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index 851e1b6c3b..9c65e818f4 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -73,7 +73,6 @@ export const gallerySlice = createSlice({ boardIdSelected: (state, action: PayloadAction) => { state.selectedBoardId = action.payload; state.galleryView = 'images'; - state.autoAssignBoardOnClick && (state.autoAddBoardId = action.payload); }, isBatchEnabledChanged: (state, action: PayloadAction) => { state.isBatchEnabled = action.payload; From 366952f810fcedd676b735046c9e384d08aa244f Mon Sep 17 00:00:00 2001 From: Kevin Brack Date: Sun, 30 Jul 2023 19:02:53 -0500 Subject: [PATCH 10/14] fix localization --- invokeai/frontend/web/public/locales/en.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index cf84e4d773..63380a19fa 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -124,7 +124,8 @@ "deleteImageBin": "Deleted images will be sent to your operating system's Bin.", "deleteImagePermanent": "Deleted images cannot be restored.", "images": "Images", - "assets": "Assets" + "assets": "Assets", + "autoAssignBoardOnClick": "Auto-Assign Board on Click" }, "hotkeys": { "keyboardShortcuts": "Keyboard Shortcuts", From 87424be95d17434bb698bb4742acbaa7fb63d2d6 Mon Sep 17 00:00:00 2001 From: Kevin Brack Date: Mon, 31 Jul 2023 19:34:24 -0500 Subject: [PATCH 11/14] block auto add board change during generation. Switch condition to isProcessing --- .../components/Boards/BoardAutoAddSelect.tsx | 12 ++++++++---- .../Boards/BoardsList/GalleryBoard.tsx | 18 ++++++++++-------- .../Boards/BoardsList/NoBoardBoard.tsx | 14 +++++++------- .../components/GallerySettingsPopover.tsx | 2 +- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx index ad0e5ab80d..9f02a29f10 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx @@ -11,11 +11,14 @@ import { useListAllBoardsQuery } from 'services/api/endpoints/boards'; const selector = createSelector( [stateSelector], - ({ gallery }) => { - const { autoAddBoardId } = gallery; + ({ gallery, system }) => { + const { autoAddBoardId, autoAssignBoardOnClick } = gallery; + const { isProcessing } = system; return { autoAddBoardId, + autoAssignBoardOnClick, + isProcessing, }; }, defaultSelectorOptions @@ -23,7 +26,8 @@ const selector = createSelector( const BoardAutoAddSelect = () => { const dispatch = useAppDispatch(); - const { autoAddBoardId } = useAppSelector(selector); + const { autoAddBoardId, autoAssignBoardOnClick, isProcessing } = + useAppSelector(selector); const inputRef = useRef(null); const { boards, hasBoards } = useListAllBoardsQuery(undefined, { selectFromResult: ({ data }) => { @@ -67,7 +71,7 @@ const BoardAutoAddSelect = () => { data={boards} nothingFound="No matching Boards" itemComponent={IAIMantineSelectItemWithTooltip} - disabled={!hasBoards} + disabled={!hasBoards || autoAssignBoardOnClick || isProcessing} filter={(value, item: SelectItem) => item.label?.toLowerCase().includes(value.toLowerCase().trim()) || item.value.toLowerCase().includes(value.toLowerCase().trim()) diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx index a4124e2393..3b591ee00f 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx @@ -27,7 +27,6 @@ import { useGetImageDTOQuery } from 'services/api/endpoints/images'; import { BoardDTO } from 'services/api/types'; import AutoAddIcon from '../AutoAddIcon'; import BoardContextMenu from '../BoardContextMenu'; -import { useIsReadyToInvoke } from 'common/hooks/useIsReadyToInvoke'; interface GalleryBoardProps { board: BoardDTO; @@ -42,19 +41,24 @@ const GalleryBoard = memo( () => createSelector( stateSelector, - ({ gallery }) => { + ({ gallery, system }) => { const isSelectedForAutoAdd = board.board_id === gallery.autoAddBoardId; const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick; + const isProcessing = system.isProcessing; - return { isSelectedForAutoAdd, autoAssignBoardOnClick }; + return { + isSelectedForAutoAdd, + autoAssignBoardOnClick, + isProcessing, + }; }, defaultSelectorOptions ), [board.board_id] ); - const { isSelectedForAutoAdd, autoAssignBoardOnClick } = + const { isSelectedForAutoAdd, autoAssignBoardOnClick, isProcessing } = useAppSelector(selector); const [isHovered, setIsHovered] = useState(false); const handleMouseOver = useCallback(() => { @@ -70,14 +74,12 @@ const GalleryBoard = memo( const { board_name, board_id } = board; const [localBoardName, setLocalBoardName] = useState(board_name); - const isReady = useIsReadyToInvoke(); - const handleSelectBoard = useCallback(() => { dispatch(boardIdSelected(board_id)); - if (autoAssignBoardOnClick && isReady) { + if (autoAssignBoardOnClick && !isProcessing) { dispatch(autoAddBoardIdChanged(board_id)); } - }, [board_id, autoAssignBoardOnClick, isReady, dispatch]); + }, [board_id, autoAssignBoardOnClick, isProcessing, dispatch]); const [updateBoard, { isLoading: isUpdateBoardLoading }] = useUpdateBoardMutation(); diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx index 3963fc04d9..118b2108f7 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx @@ -15,31 +15,31 @@ import { memo, useCallback, useMemo, useState } from 'react'; import { useBoardName } from 'services/api/hooks/useBoardName'; import AutoAddIcon from '../AutoAddIcon'; import BoardContextMenu from '../BoardContextMenu'; -import { useIsReadyToInvoke } from 'common/hooks/useIsReadyToInvoke'; interface Props { isSelected: boolean; } const selector = createSelector( stateSelector, - ({ gallery }) => { + ({ gallery, system }) => { const { autoAddBoardId, autoAssignBoardOnClick } = gallery; - return { autoAddBoardId, autoAssignBoardOnClick }; + const { isProcessing } = system; + return { autoAddBoardId, autoAssignBoardOnClick, isProcessing }; }, defaultSelectorOptions ); const NoBoardBoard = memo(({ isSelected }: Props) => { const dispatch = useAppDispatch(); - const { autoAddBoardId, autoAssignBoardOnClick } = useAppSelector(selector); + const { autoAddBoardId, autoAssignBoardOnClick, isProcessing } = + useAppSelector(selector); const boardName = useBoardName(undefined); - const isReady = useIsReadyToInvoke(); const handleSelectBoard = useCallback(() => { dispatch(boardIdSelected(undefined)); - if (autoAssignBoardOnClick && isReady) { + if (autoAssignBoardOnClick && !isProcessing) { dispatch(autoAddBoardIdChanged(undefined)); } - }, [dispatch, autoAssignBoardOnClick, isReady]); + }, [dispatch, autoAssignBoardOnClick, isProcessing]); const [isHovered, setIsHovered] = useState(false); const handleMouseOver = useCallback(() => { setIsHovered(true); diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx b/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx index 04cc98edb7..796cc542e7 100644 --- a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx @@ -82,7 +82,7 @@ const GallerySettingsPopover = () => { dispatch(autoAssignBoardOnClickChanged(e.target.checked)) } /> - {!autoAssignBoardOnClick && } + ); From 26ef5249b1902132493ef6cbc5f1c8d6c4c7bdd8 Mon Sep 17 00:00:00 2001 From: Kevin Brack Date: Mon, 31 Jul 2023 19:44:41 -0500 Subject: [PATCH 12/14] guard board switching in board context menu --- .../gallery/components/Boards/BoardContextMenu.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx index 35fcbd87f7..2774288612 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx @@ -25,14 +25,17 @@ const BoardContextMenu = memo( const selector = useMemo( () => - createSelector(stateSelector, ({ gallery }) => { + createSelector(stateSelector, ({ gallery, system }) => { const isAutoAdd = gallery.autoAddBoardId === board_id; - return { isAutoAdd }; + const isProcessing = system.isProcessing; + const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick; + return { isAutoAdd, isProcessing, autoAssignBoardOnClick }; }), [board_id] ); - const { isAutoAdd } = useAppSelector(selector); + const { isAutoAdd, isProcessing, autoAssignBoardOnClick } = + useAppSelector(selector); const boardName = useBoardName(board_id); const handleSetAutoAdd = useCallback(() => { @@ -59,7 +62,7 @@ const BoardContextMenu = memo( } - isDisabled={isAutoAdd} + isDisabled={isAutoAdd || isProcessing || autoAssignBoardOnClick} onClick={handleSetAutoAdd} > Auto-add to this Board From 7021467048925d413e9fd132545491ee04ac603e Mon Sep 17 00:00:00 2001 From: Eugene Brodsky Date: Wed, 2 Aug 2023 19:46:02 -0400 Subject: [PATCH 13/14] (ci) do not install all dependencies when running static checks (#4036) Co-authored-by: psychedelicious <4822129+psychedelicious@users.noreply.github.com> --- .github/workflows/style-checks.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/style-checks.yml b/.github/workflows/style-checks.yml index 8aceb6469e..0bb19e95e5 100644 --- a/.github/workflows/style-checks.yml +++ b/.github/workflows/style-checks.yml @@ -1,13 +1,15 @@ -name: Black # TODO: add isort and flake8 later +name: style checks +# just formatting for now +# TODO: add isort and flake8 later on: - pull_request: {} + pull_request: push: - branches: master + branches: main tags: "*" jobs: - test: + black: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -19,8 +21,7 @@ jobs: - name: Install dependencies with pip run: | - pip install --upgrade pip wheel - pip install .[test] + pip install black # - run: isort --check-only . - run: black --check . From 4e0949fa5503c4ed2a61641aa9deb0f3e1134fb4 Mon Sep 17 00:00:00 2001 From: Damian Stewart Date: Sun, 30 Jul 2023 20:12:53 +0200 Subject: [PATCH 14/14] fix .swap() by reverting improperly merged @classmethod change --- .../diffusion/shared_invokeai_diffusion.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py b/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py index 272518e928..c01cf82c57 100644 --- a/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py +++ b/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py @@ -78,10 +78,9 @@ class InvokeAIDiffuserComponent: self.cross_attention_control_context = None self.sequential_guidance = config.sequential_guidance - @classmethod @contextmanager def custom_attention_context( - cls, + self, unet: UNet2DConditionModel, # note: also may futz with the text encoder depending on requested LoRAs extra_conditioning_info: Optional[ExtraConditioningInfo], step_count: int, @@ -91,18 +90,19 @@ class InvokeAIDiffuserComponent: old_attn_processors = unet.attn_processors # Load lora conditions into the model if extra_conditioning_info.wants_cross_attention_control: - cross_attention_control_context = Context( + self.cross_attention_control_context = Context( arguments=extra_conditioning_info.cross_attention_control_args, step_count=step_count, ) setup_cross_attention_control_attention_processors( unet, - cross_attention_control_context, + self.cross_attention_control_context, ) try: yield None finally: + self.cross_attention_control_context = None if old_attn_processors is not None: unet.set_attn_processor(old_attn_processors) # TODO resuscitate attention map saving