Merge branch 'main' into docs/update-contributors

This commit is contained in:
Millun Atluri 2024-01-28 11:26:35 -05:00 committed by GitHub
commit 51bdf2fd19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
635 changed files with 5185 additions and 12391 deletions

BIN
docs/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -117,6 +117,11 @@ Mac and Linux machines, and runs on GPU cards with as little as 4 GB of RAM.
## :octicons-gift-24: InvokeAI Features ## :octicons-gift-24: InvokeAI Features
### Installation
- [Automated Installer](installation/010_INSTALL_AUTOMATED.md)
- [Manual Installation](installation/020_INSTALL_MANUAL.md)
- [Docker Installation](installation/040_INSTALL_DOCKER.md)
### The InvokeAI Web Interface ### The InvokeAI Web Interface
- [WebUI overview](features/WEB.md) - [WebUI overview](features/WEB.md)
- [WebUI hotkey reference guide](features/WEBUIHOTKEYS.md) - [WebUI hotkey reference guide](features/WEBUIHOTKEYS.md)

View File

@ -18,13 +18,18 @@ either an Nvidia-based card (with CUDA support) or an AMD card (using the ROCm
driver). driver).
## **[Automated Installer](010_INSTALL_AUTOMATED.md)** ## **[Automated Installer (Recommended)](010_INSTALL_AUTOMATED.md)**
✅ This is the recommended installation method for first-time users. ✅ This is the recommended installation method for first-time users.
This is a script that will install all of InvokeAI's essential This is a script that will install all of InvokeAI's essential
third party libraries and InvokeAI itself. It includes access to a third party libraries and InvokeAI itself.
"developer console" which will help us debug problems with you and
give you to access experimental features. 🖥️ **Download the latest installer .zip file here** : https://github.com/invoke-ai/InvokeAI/releases/latest
- *Look for the file labelled "InvokeAI-installer-v3.X.X.zip" at the bottom of the page*
- If you experience issues, read through the full [installation instructions](010_INSTALL_AUTOMATED.md) to make sure you have met all of the installation requirements. If you need more help, join the [Discord](discord.gg/invoke-ai) or create an issue on [Github](https://github.com/invoke-ai/InvokeAI).
## **[Manual Installation](020_INSTALL_MANUAL.md)** ## **[Manual Installation](020_INSTALL_MANUAL.md)**
This method is recommended for experienced users and developers. This method is recommended for experienced users and developers.

File diff suppressed because it is too large Load Diff

View File

@ -1,131 +1,9 @@
module.exports = { module.exports = {
env: { extends: ['@invoke-ai/eslint-config-react'],
browser: true,
es6: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:react/jsx-runtime',
'prettier',
'plugin:storybook/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2018,
sourceType: 'module',
},
plugins: [
'react',
'@typescript-eslint',
'eslint-plugin-react-hooks',
'i18next',
'path',
'unused-imports',
'simple-import-sort',
'eslint-plugin-import',
// These rules are too strict for normal usage, but are useful for optimizing rerenders
// '@arthurgeron/react-usememo',
],
root: true,
rules: { rules: {
'path/no-relative-imports': ['error', { maxDepth: 0 }], // TODO(psyche): Enable this rule. Requires no default exports in components - many changes.
curly: 'error', 'react-refresh/only-export-components': 'off',
'i18next/no-literal-string': 'warn', // TODO(psyche): Enable this rule. Requires a lot of eslint-disable-next-line comments.
'react/jsx-no-bind': ['error', { allowBind: true }], '@typescript-eslint/consistent-type-assertions': 'off',
'react/jsx-curly-brace-presence': [
'error',
{ props: 'never', children: 'never' },
],
'react-hooks/exhaustive-deps': 'error',
'no-var': 'error',
'brace-style': 'error',
'prefer-template': 'error',
'import/no-duplicates': 'error',
radix: 'error',
'space-before-blocks': 'error',
'import/prefer-default-export': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': [
'warn',
{
vars: 'all',
varsIgnorePattern: '^_',
args: 'after-used',
argsIgnorePattern: '^_',
},
],
// These rules are too strict for normal usage, but are useful for optimizing rerenders
// '@arthurgeron/react-usememo/require-usememo': [
// 'warn',
// {
// strict: false,
// checkHookReturnObject: false,
// fix: { addImports: true },
// checkHookCalls: false,
// },
// ],
// '@arthurgeron/react-usememo/require-memo': 'warn',
'@typescript-eslint/ban-ts-comment': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-empty-interface': [
'error',
{
allowSingleExtends: true,
},
],
'@typescript-eslint/consistent-type-imports': [
'error',
{
prefer: 'type-imports',
fixStyle: 'separate-type-imports',
disallowTypeAnnotations: true,
},
],
'@typescript-eslint/no-import-type-side-effects': 'error',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
// Prefer @invoke-ai/ui components over chakra
'no-restricted-imports': 'off',
'@typescript-eslint/no-restricted-imports': [
'warn',
{
paths: [
{
name: '@chakra-ui/react',
message: "Please import from '@invoke-ai/ui' instead.",
},
{
name: '@chakra-ui/layout',
message: "Please import from '@invoke-ai/ui' instead.",
},
{
name: '@chakra-ui/portal',
message: "Please import from '@invoke-ai/ui' instead.",
},
],
},
],
},
overrides: [
{
files: ['*.stories.tsx'],
rules: {
'i18next/no-literal-string': 'off',
},
},
],
settings: {
react: {
version: 'detect',
},
}, },
}; };

View File

@ -1,9 +1,5 @@
module.exports = { module.exports = {
trailingComma: 'es5', ...require('@invoke-ai/prettier-config-react'),
tabWidth: 2,
semi: true,
singleQuote: true,
endOfLine: 'auto',
overrides: [ overrides: [
{ {
files: ['public/locales/*.json'], files: ['public/locales/*.json'],

View File

@ -1,7 +1,7 @@
import { PropsWithChildren, memo, useEffect } from 'react'; import { PropsWithChildren, memo, useEffect } from 'react';
import { modelChanged } from '../src/features/parameters/store/generationSlice'; import { modelChanged } from '../src/features/parameters/store/generationSlice';
import { useAppDispatch } from '../src/app/store/storeHooks'; import { useAppDispatch } from '../src/app/store/storeHooks';
import { useGlobalModifiersInit } from '@invoke-ai/ui'; import { useGlobalModifiersInit } from '@invoke-ai/ui-library';
/** /**
* Initializes some state for storybook. Must be in a different component * Initializes some state for storybook. Must be in a different component
* so that it is run inside the redux context. * so that it is run inside the redux context.

View File

@ -1,13 +1,7 @@
{ {
"entry": ["src/main.tsx"], "entry": ["src/main.tsx"],
"extensions": [".ts", ".tsx"], "extensions": [".ts", ".tsx"],
"ignorePatterns": [ "ignorePatterns": ["**/node_modules/**", "dist/**", "public/**", "**/*.stories.tsx", "config/**"],
"**/node_modules/**",
"dist/**",
"public/**",
"**/*.stories.tsx",
"config/**"
],
"ignoreUnresolved": [], "ignoreUnresolved": [],
"ignoreUnimported": ["src/i18.d.ts", "vite.config.ts", "src/vite-env.d.ts"], "ignoreUnimported": ["src/i18.d.ts", "vite.config.ts", "src/vite-env.d.ts"],
"respectGitignore": true, "respectGitignore": true,

View File

@ -19,8 +19,8 @@
"dist" "dist"
], ],
"scripts": { "scripts": {
"dev": "concurrently \"vite dev\" \"pnpm run theme:watch\"", "dev": "vite dev",
"dev:host": "concurrently \"vite dev --host\" \"pnpm run theme:watch\"", "dev:host": "vite dev --host",
"build": "pnpm run lint && vite build", "build": "pnpm run lint && vite build",
"typegen": "node scripts/typegen.js", "typegen": "node scripts/typegen.js",
"preview": "vite preview", "preview": "vite preview",
@ -31,9 +31,6 @@
"lint": "concurrently -g -n eslint,prettier,tsc,madge -c cyan,green,magenta,yellow \"pnpm run lint:eslint\" \"pnpm run lint:prettier\" \"pnpm run lint:tsc\" \"pnpm run lint:madge\"", "lint": "concurrently -g -n eslint,prettier,tsc,madge -c cyan,green,magenta,yellow \"pnpm run lint:eslint\" \"pnpm run lint:prettier\" \"pnpm run lint:tsc\" \"pnpm run lint:madge\"",
"fix": "eslint --fix . && prettier --log-level warn --write .", "fix": "eslint --fix . && prettier --log-level warn --write .",
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"postinstall": "pnpm run theme",
"theme": "chakra-cli tokens node_modules/@invoke-ai/ui",
"theme:watch": "chakra-cli tokens node_modules/@invoke-ai/ui --watch",
"storybook": "storybook dev -p 6006", "storybook": "storybook dev -p 6006",
"build-storybook": "storybook build", "build-storybook": "storybook build",
"unimported": "npx unimported" "unimported": "npx unimported"
@ -57,7 +54,7 @@
"@dnd-kit/core": "^6.1.0", "@dnd-kit/core": "^6.1.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
"@fontsource-variable/inter": "^5.0.16", "@fontsource-variable/inter": "^5.0.16",
"@invoke-ai/ui": "0.0.13", "@invoke-ai/ui-library": "^0.0.18",
"@mantine/form": "6.0.21", "@mantine/form": "6.0.21",
"@nanostores/react": "^0.7.1", "@nanostores/react": "^0.7.1",
"@reduxjs/toolkit": "2.0.1", "@reduxjs/toolkit": "2.0.1",
@ -107,7 +104,6 @@
"zod-validation-error": "^3.0.0" "zod-validation-error": "^3.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@chakra-ui/cli": "^2.4.1",
"@chakra-ui/react": "^2.8.2", "@chakra-ui/react": "^2.8.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -115,7 +111,8 @@
}, },
"devDependencies": { "devDependencies": {
"@arthurgeron/eslint-plugin-react-usememo": "^2.2.3", "@arthurgeron/eslint-plugin-react-usememo": "^2.2.3",
"@chakra-ui/cli": "^2.4.1", "@invoke-ai/eslint-config-react": "^0.0.12",
"@invoke-ai/prettier-config-react": "^0.0.6",
"@storybook/addon-docs": "^7.6.10", "@storybook/addon-docs": "^7.6.10",
"@storybook/addon-essentials": "^7.6.10", "@storybook/addon-essentials": "^7.6.10",
"@storybook/addon-interactions": "^7.6.10", "@storybook/addon-interactions": "^7.6.10",

View File

@ -28,9 +28,9 @@ dependencies:
'@fontsource-variable/inter': '@fontsource-variable/inter':
specifier: ^5.0.16 specifier: ^5.0.16
version: 5.0.16 version: 5.0.16
'@invoke-ai/ui': '@invoke-ai/ui-library':
specifier: 0.0.13 specifier: ^0.0.18
version: 0.0.13(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.16)(@internationalized/date@3.5.1)(@types/react@18.2.48)(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0) version: 0.0.18(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.16)(@internationalized/date@3.5.1)(@types/react@18.2.48)(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0)
'@mantine/form': '@mantine/form':
specifier: 6.0.21 specifier: 6.0.21
version: 6.0.21(react@18.2.0) version: 6.0.21(react@18.2.0)
@ -177,9 +177,12 @@ devDependencies:
'@arthurgeron/eslint-plugin-react-usememo': '@arthurgeron/eslint-plugin-react-usememo':
specifier: ^2.2.3 specifier: ^2.2.3
version: 2.2.3 version: 2.2.3
'@chakra-ui/cli': '@invoke-ai/eslint-config-react':
specifier: ^2.4.1 specifier: ^0.0.12
version: 2.4.1 version: 0.0.12(@typescript-eslint/eslint-plugin@6.19.0)(@typescript-eslint/parser@6.19.0)(eslint-config-prettier@9.1.0)(eslint-plugin-i18next@6.0.3)(eslint-plugin-import@2.29.1)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react-refresh@0.4.5)(eslint-plugin-react@7.33.2)(eslint-plugin-simple-import-sort@10.0.0)(eslint-plugin-storybook@0.6.15)(eslint-plugin-unused-imports@3.0.0)(eslint@8.56.0)
'@invoke-ai/prettier-config-react':
specifier: ^0.0.6
version: 0.0.6(prettier@3.2.4)
'@storybook/addon-docs': '@storybook/addon-docs':
specifier: ^7.6.10 specifier: ^7.6.10
version: 7.6.10(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) version: 7.6.10(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
@ -1852,19 +1855,6 @@ packages:
react: 18.2.0 react: 18.2.0
dev: false dev: false
/@chakra-ui/cli@2.4.1:
resolution: {integrity: sha512-GZZuHUA1cXJWpmYNiVTLPihvY4VhIssRl+AXgw/0IbeodTMop3jWlIioPKLAQeXu5CwvRA6iESyGjnu1V8Zykg==}
hasBin: true
dependencies:
chokidar: 3.5.3
cli-check-node: 1.3.4
cli-handle-unhandled: 1.1.1
cli-welcome: 2.2.2
commander: 9.5.0
esbuild: 0.17.19
prettier: 2.8.8
dev: true
/@chakra-ui/clickable@2.1.0(react@18.2.0): /@chakra-ui/clickable@2.1.0(react@18.2.0):
resolution: {integrity: sha512-flRA/ClPUGPYabu+/GLREZVZr9j2uyyazCAUHAdrTUEdDYCr31SVGhgh7dgKdtq23bOvAQJpIJjw/0Bs0WvbXw==} resolution: {integrity: sha512-flRA/ClPUGPYabu+/GLREZVZr9j2uyyazCAUHAdrTUEdDYCr31SVGhgh7dgKdtq23bOvAQJpIJjw/0Bs0WvbXw==}
peerDependencies: peerDependencies:
@ -2907,7 +2897,7 @@ packages:
resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
dependencies: dependencies:
'@babel/helper-module-imports': 7.22.15 '@babel/helper-module-imports': 7.22.15
'@babel/runtime': 7.23.6 '@babel/runtime': 7.23.8
'@emotion/hash': 0.9.1 '@emotion/hash': 0.9.1
'@emotion/memoize': 0.8.1 '@emotion/memoize': 0.8.1
'@emotion/serialize': 1.1.3 '@emotion/serialize': 1.1.3
@ -2966,7 +2956,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': 7.23.6 '@babel/runtime': 7.23.8
'@emotion/babel-plugin': 11.11.0 '@emotion/babel-plugin': 11.11.0
'@emotion/cache': 11.11.0 '@emotion/cache': 11.11.0
'@emotion/serialize': 1.1.3 '@emotion/serialize': 1.1.3
@ -3041,15 +3031,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/android-arm64@0.17.19:
resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm64@0.18.20: /@esbuild/android-arm64@0.18.20:
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3068,15 +3049,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/android-arm@0.17.19:
resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm@0.18.20: /@esbuild/android-arm@0.18.20:
resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3095,15 +3067,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/android-x64@0.17.19:
resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64@0.18.20: /@esbuild/android-x64@0.18.20:
resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3122,15 +3085,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/darwin-arm64@0.17.19:
resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64@0.18.20: /@esbuild/darwin-arm64@0.18.20:
resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3149,15 +3103,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/darwin-x64@0.17.19:
resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64@0.18.20: /@esbuild/darwin-x64@0.18.20:
resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3176,15 +3121,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/freebsd-arm64@0.17.19:
resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64@0.18.20: /@esbuild/freebsd-arm64@0.18.20:
resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3203,15 +3139,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/freebsd-x64@0.17.19:
resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64@0.18.20: /@esbuild/freebsd-x64@0.18.20:
resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3230,15 +3157,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/linux-arm64@0.17.19:
resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64@0.18.20: /@esbuild/linux-arm64@0.18.20:
resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3257,15 +3175,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/linux-arm@0.17.19:
resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm@0.18.20: /@esbuild/linux-arm@0.18.20:
resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3284,15 +3193,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/linux-ia32@0.17.19:
resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32@0.18.20: /@esbuild/linux-ia32@0.18.20:
resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3311,15 +3211,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/linux-loong64@0.17.19:
resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64@0.18.20: /@esbuild/linux-loong64@0.18.20:
resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3338,15 +3229,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/linux-mips64el@0.17.19:
resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el@0.18.20: /@esbuild/linux-mips64el@0.18.20:
resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3365,15 +3247,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/linux-ppc64@0.17.19:
resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64@0.18.20: /@esbuild/linux-ppc64@0.18.20:
resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3392,15 +3265,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/linux-riscv64@0.17.19:
resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64@0.18.20: /@esbuild/linux-riscv64@0.18.20:
resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3419,15 +3283,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/linux-s390x@0.17.19:
resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x@0.18.20: /@esbuild/linux-s390x@0.18.20:
resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3446,15 +3301,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/linux-x64@0.17.19:
resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64@0.18.20: /@esbuild/linux-x64@0.18.20:
resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3473,15 +3319,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/netbsd-x64@0.17.19:
resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64@0.18.20: /@esbuild/netbsd-x64@0.18.20:
resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3500,15 +3337,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/openbsd-x64@0.17.19:
resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64@0.18.20: /@esbuild/openbsd-x64@0.18.20:
resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3527,15 +3355,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/sunos-x64@0.17.19:
resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64@0.18.20: /@esbuild/sunos-x64@0.18.20:
resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3554,15 +3373,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/win32-arm64@0.17.19:
resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64@0.18.20: /@esbuild/win32-arm64@0.18.20:
resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3581,15 +3391,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/win32-ia32@0.17.19:
resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32@0.18.20: /@esbuild/win32-ia32@0.18.20:
resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3608,15 +3409,6 @@ packages:
dev: true dev: true
optional: true optional: true
/@esbuild/win32-x64@0.17.19:
resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64@0.18.20: /@esbuild/win32-x64@0.18.20:
resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3759,8 +3551,46 @@ packages:
'@swc/helpers': 0.5.3 '@swc/helpers': 0.5.3
dev: false dev: false
/@invoke-ai/ui@0.0.13(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.16)(@internationalized/date@3.5.1)(@types/react@18.2.48)(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0): /@invoke-ai/eslint-config-react@0.0.12(@typescript-eslint/eslint-plugin@6.19.0)(@typescript-eslint/parser@6.19.0)(eslint-config-prettier@9.1.0)(eslint-plugin-i18next@6.0.3)(eslint-plugin-import@2.29.1)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react-refresh@0.4.5)(eslint-plugin-react@7.33.2)(eslint-plugin-simple-import-sort@10.0.0)(eslint-plugin-storybook@0.6.15)(eslint-plugin-unused-imports@3.0.0)(eslint@8.56.0):
resolution: {integrity: sha512-X4Txij2dMnzPUXTPhorBHezByJQ/ceyHxCM+zZ0gpFsSyXUieOFWjaSu+dAVpghS9y0dxFQGayHvNyX6VsX/PA==} resolution: {integrity: sha512-6IXENcSa7vv+YPO/TYmC8qXXJFQt3JqDY+Yc1AMf4/d3b3o+CA7/mqepXIhydG9Gqo5jTRknXdDmjSaLxgCJ/g==}
peerDependencies:
'@typescript-eslint/eslint-plugin': ^6.19.0
'@typescript-eslint/parser': ^6.19.0
eslint: ^8.56.0
eslint-config-prettier: ^9.1.0
eslint-plugin-i18next: ^6.0.3
eslint-plugin-import: ^2.29.1
eslint-plugin-react: ^7.33.2
eslint-plugin-react-hooks: ^4.6.0
eslint-plugin-react-refresh: ^0.4.5
eslint-plugin-simple-import-sort: ^10.0.0
eslint-plugin-storybook: ^0.6.15
eslint-plugin-unused-imports: ^3.0.0
dependencies:
'@typescript-eslint/eslint-plugin': 6.19.0(@typescript-eslint/parser@6.19.0)(eslint@8.56.0)(typescript@5.3.3)
'@typescript-eslint/parser': 6.19.0(eslint@8.56.0)(typescript@5.3.3)
eslint: 8.56.0
eslint-config-prettier: 9.1.0(eslint@8.56.0)
eslint-plugin-i18next: 6.0.3
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.0)(eslint@8.56.0)
eslint-plugin-react: 7.33.2(eslint@8.56.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.56.0)
eslint-plugin-react-refresh: 0.4.5(eslint@8.56.0)
eslint-plugin-simple-import-sort: 10.0.0(eslint@8.56.0)
eslint-plugin-storybook: 0.6.15(eslint@8.56.0)(typescript@5.3.3)
eslint-plugin-unused-imports: 3.0.0(@typescript-eslint/eslint-plugin@6.19.0)(eslint@8.56.0)
dev: true
/@invoke-ai/prettier-config-react@0.0.6(prettier@3.2.4):
resolution: {integrity: sha512-qHE6GAw/Aka/8TLTN9U1U+8pxjaFe5irDv/uSgzqmrBR1rGiVyMp19pEficWRRt+03zYdquiiDjTmoabWQxY0Q==}
peerDependencies:
prettier: ^3.2.4
dependencies:
prettier: 3.2.4
dev: true
/@invoke-ai/ui-library@0.0.18(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.16)(@internationalized/date@3.5.1)(@types/react@18.2.48)(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Yme+2+pzYy3TPb7ZT0hYmBwahH29ZRSVIxLKSexh3BsbJXbTzGssRQU78QvK6Ymxemgbso3P8Rs+IW0zNhQKjQ==}
peerDependencies: peerDependencies:
'@fontsource-variable/inter': ^5.0.16 '@fontsource-variable/inter': ^5.0.16
react: ^18.2.0 react: ^18.2.0
@ -3782,11 +3612,12 @@ packages:
framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0)
lodash-es: 4.17.21 lodash-es: 4.17.21
nanostores: 0.9.5 nanostores: 0.9.5
overlayscrollbars: 2.4.6 overlayscrollbars: 2.4.7
overlayscrollbars-react: 0.5.3(overlayscrollbars@2.4.6)(react@18.2.0) overlayscrollbars-react: 0.5.4(overlayscrollbars@2.4.7)(react@18.2.0)
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
react-i18next: 14.0.0(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0) react-i18next: 14.0.1(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0)
react-icons: 5.0.1(react@18.2.0)
react-select: 5.8.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) react-select: 5.8.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@chakra-ui/form-control' - '@chakra-ui/form-control'
@ -7640,7 +7471,7 @@ packages:
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
engines: {node: '>=10', npm: '>=6'} engines: {node: '>=10', npm: '>=6'}
dependencies: dependencies:
'@babel/runtime': 7.23.6 '@babel/runtime': 7.23.8
cosmiconfig: 7.1.0 cosmiconfig: 7.1.0
resolve: 1.22.8 resolve: 1.22.8
dev: false dev: false
@ -7959,17 +7790,6 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
/clear-any-console@1.16.2:
resolution: {integrity: sha512-OL/7wZpNy9x0GBSzz3poWja84Nr7iaH8aYNsJ5Uet2BVLj6Lm1zvWpZN/yH46Vv3ae7YfHmLLMmfHj911fshJg==}
dev: true
/cli-check-node@1.3.4:
resolution: {integrity: sha512-iLGgQXm82iP8eH3R67qbOWs5qqUOLmNnMy5Lzl/RybcMh3y+H2zWU5POzuQ6oDUOdz4XWuxcFhP75szqd6frLg==}
dependencies:
chalk: 3.0.0
log-symbols: 3.0.0
dev: true
/cli-cursor@3.1.0: /cli-cursor@3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -7977,19 +7797,6 @@ packages:
restore-cursor: 3.1.0 restore-cursor: 3.1.0
dev: true dev: true
/cli-handle-error@4.4.0:
resolution: {integrity: sha512-RyBCnKlc7xVr79cKb9RfBq+4fjwQeX8HKeNzIPnI/W+DWWIUUKh2ur576DpwJ3kZt2UGHlIAOF7N9txy+mgZsA==}
dependencies:
chalk: 3.0.0
log-symbols: 3.0.0
dev: true
/cli-handle-unhandled@1.1.1:
resolution: {integrity: sha512-Em91mJvU7VdgT2MxQpyY633vW1tDzRjPDbii6ZjEBHHLLh0xDoVkFt/wjvi9nSvJcz9rJmvtJSK8KL/hvF0Stg==}
dependencies:
cli-handle-error: 4.4.0
dev: true
/cli-spinners@2.9.2: /cli-spinners@2.9.2:
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -8004,14 +7811,6 @@ packages:
'@colors/colors': 1.5.0 '@colors/colors': 1.5.0
dev: true dev: true
/cli-welcome@2.2.2:
resolution: {integrity: sha512-LgDGS0TW4nIf8v81wpuZzfOEDPcy68u0jKR0Fy5IaWftqdminI6FoDiMFt1mjPylqKGNv/wFsZ7fCs93IeDMIw==}
dependencies:
chalk: 2.4.2
clear-any-console: 1.16.2
prettier: 2.8.8
dev: true
/cliui@8.0.1: /cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -8968,36 +8767,6 @@ packages:
- supports-color - supports-color
dev: true dev: true
/esbuild@0.17.19:
resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.17.19
'@esbuild/android-arm64': 0.17.19
'@esbuild/android-x64': 0.17.19
'@esbuild/darwin-arm64': 0.17.19
'@esbuild/darwin-x64': 0.17.19
'@esbuild/freebsd-arm64': 0.17.19
'@esbuild/freebsd-x64': 0.17.19
'@esbuild/linux-arm': 0.17.19
'@esbuild/linux-arm64': 0.17.19
'@esbuild/linux-ia32': 0.17.19
'@esbuild/linux-loong64': 0.17.19
'@esbuild/linux-mips64el': 0.17.19
'@esbuild/linux-ppc64': 0.17.19
'@esbuild/linux-riscv64': 0.17.19
'@esbuild/linux-s390x': 0.17.19
'@esbuild/linux-x64': 0.17.19
'@esbuild/netbsd-x64': 0.17.19
'@esbuild/openbsd-x64': 0.17.19
'@esbuild/sunos-x64': 0.17.19
'@esbuild/win32-arm64': 0.17.19
'@esbuild/win32-ia32': 0.17.19
'@esbuild/win32-x64': 0.17.19
dev: true
/esbuild@0.18.20: /esbuild@0.18.20:
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -9198,6 +8967,14 @@ packages:
eslint: 8.56.0 eslint: 8.56.0
dev: true dev: true
/eslint-plugin-react-refresh@0.4.5(eslint@8.56.0):
resolution: {integrity: sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==}
peerDependencies:
eslint: '>=7'
dependencies:
eslint: 8.56.0
dev: true
/eslint-plugin-react@7.33.2(eslint@8.56.0): /eslint-plugin-react@7.33.2(eslint@8.56.0):
resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -10908,13 +10685,6 @@ packages:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: true dev: true
/log-symbols@3.0.0:
resolution: {integrity: sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==}
engines: {node: '>=8'}
dependencies:
chalk: 2.4.2
dev: true
/log-symbols@4.1.0: /log-symbols@4.1.0:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -11565,10 +11335,24 @@ packages:
react: 18.2.0 react: 18.2.0
dev: false dev: false
/overlayscrollbars-react@0.5.4(overlayscrollbars@2.4.7)(react@18.2.0):
resolution: {integrity: sha512-FPKx9XnXovTnI4+2JXig5uEaTLSEJ6svOwPzIfBBXTHBRNsz2+WhYUmfM0K/BNYxjgDEwuPm+NQhEoOA0RoG1g==}
peerDependencies:
overlayscrollbars: ^2.0.0
react: '>=16.8.0'
dependencies:
overlayscrollbars: 2.4.7
react: 18.2.0
dev: false
/overlayscrollbars@2.4.6: /overlayscrollbars@2.4.6:
resolution: {integrity: sha512-C7tmhetwMv9frEvIT/RfkAVEgbjRNz/Gh2zE8BVmN+jl35GRaAnz73rlGQCMRoC2arpACAXyMNnJkzHb7GBrcA==} resolution: {integrity: sha512-C7tmhetwMv9frEvIT/RfkAVEgbjRNz/Gh2zE8BVmN+jl35GRaAnz73rlGQCMRoC2arpACAXyMNnJkzHb7GBrcA==}
dev: false dev: false
/overlayscrollbars@2.4.7:
resolution: {integrity: sha512-02X2/nHno35dzebCx+EO2tRDaKAOltZqUKdUqvq3Pt8htCuhJbYi+mjr0CYerVeGRRoZ2Uo6/8XrNg//DJJ+GA==}
dev: false
/p-limit@2.3.0: /p-limit@2.3.0:
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -12207,6 +11991,26 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/react-i18next@14.0.1(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-TMV8hFismBmpMdIehoFHin/okfvgjFhp723RYgIqB4XyhDobVMyukyM3Z8wtTRmajyFMZrBl/OaaXF2P6WjUAw==}
peerDependencies:
i18next: '>= 23.2.3'
react: '>= 16.8.0'
react-dom: '*'
react-native: '*'
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
dependencies:
'@babel/runtime': 7.23.8
html-parse-stringify: 3.0.1
i18next: 23.7.16
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/react-icons@5.0.1(react@18.2.0): /react-icons@5.0.1(react@18.2.0):
resolution: {integrity: sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==} resolution: {integrity: sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==}
peerDependencies: peerDependencies:

View File

@ -1463,9 +1463,7 @@
}, },
"compositingCoherencePass": { "compositingCoherencePass": {
"heading": "Coherence Pass", "heading": "Coherence Pass",
"paragraphs": [ "paragraphs": ["A second round of denoising helps to composite the Inpainted/Outpainted image."]
"A second round of denoising helps to composite the Inpainted/Outpainted image."
]
}, },
"compositingCoherenceMode": { "compositingCoherenceMode": {
"heading": "Mode", "heading": "Mode",
@ -1473,10 +1471,7 @@
}, },
"compositingCoherenceSteps": { "compositingCoherenceSteps": {
"heading": "Steps", "heading": "Steps",
"paragraphs": [ "paragraphs": ["Number of denoising steps used in the Coherence Pass.", "Same as the main Steps parameter."]
"Number of denoising steps used in the Coherence Pass.",
"Same as the main Steps parameter."
]
}, },
"compositingStrength": { "compositingStrength": {
"heading": "Strength", "heading": "Strength",
@ -1498,15 +1493,11 @@
}, },
"controlNetControlMode": { "controlNetControlMode": {
"heading": "Control Mode", "heading": "Control Mode",
"paragraphs": [ "paragraphs": ["Lends more weight to either the prompt or ControlNet."]
"Lends more weight to either the prompt or ControlNet."
]
}, },
"controlNetResizeMode": { "controlNetResizeMode": {
"heading": "Resize Mode", "heading": "Resize Mode",
"paragraphs": [ "paragraphs": ["How the ControlNet image will be fit to the image output size."]
"How the ControlNet image will be fit to the image output size."
]
}, },
"controlNet": { "controlNet": {
"heading": "ControlNet", "heading": "ControlNet",
@ -1516,9 +1507,7 @@
}, },
"controlNetWeight": { "controlNetWeight": {
"heading": "Weight", "heading": "Weight",
"paragraphs": [ "paragraphs": ["How strongly the ControlNet will impact the generated image."]
"How strongly the ControlNet will impact the generated image."
]
}, },
"dynamicPrompts": { "dynamicPrompts": {
"heading": "Dynamic Prompts", "heading": "Dynamic Prompts",
@ -1530,9 +1519,7 @@
}, },
"dynamicPromptsMaxPrompts": { "dynamicPromptsMaxPrompts": {
"heading": "Max Prompts", "heading": "Max Prompts",
"paragraphs": [ "paragraphs": ["Limits the number of prompts that can be generated by Dynamic Prompts."]
"Limits the number of prompts that can be generated by Dynamic Prompts."
]
}, },
"dynamicPromptsSeedBehaviour": { "dynamicPromptsSeedBehaviour": {
"heading": "Seed Behaviour", "heading": "Seed Behaviour",
@ -1549,9 +1536,7 @@
}, },
"lora": { "lora": {
"heading": "LoRA Weight", "heading": "LoRA Weight",
"paragraphs": [ "paragraphs": ["Higher LoRA weight will lead to larger impacts on the final image."]
"Higher LoRA weight will lead to larger impacts on the final image."
]
}, },
"noiseUseCPU": { "noiseUseCPU": {
"heading": "Use CPU Noise", "heading": "Use CPU Noise",
@ -1563,9 +1548,7 @@
}, },
"paramCFGScale": { "paramCFGScale": {
"heading": "CFG Scale", "heading": "CFG Scale",
"paragraphs": [ "paragraphs": ["Controls how much your prompt influences the generation process."]
"Controls how much your prompt influences the generation process."
]
}, },
"paramCFGRescaleMultiplier": { "paramCFGRescaleMultiplier": {
"heading": "CFG Rescale Multiplier", "heading": "CFG Rescale Multiplier",
@ -1617,9 +1600,7 @@
}, },
"paramVAE": { "paramVAE": {
"heading": "VAE", "heading": "VAE",
"paragraphs": [ "paragraphs": ["Model used for translating AI output into the final image."]
"Model used for translating AI output into the final image."
]
}, },
"paramVAEPrecision": { "paramVAEPrecision": {
"heading": "VAE Precision", "heading": "VAE Precision",

View File

@ -384,7 +384,11 @@
"desc": "Apre e chiude le opzioni e i pannelli della galleria", "desc": "Apre e chiude le opzioni e i pannelli della galleria",
"title": "Attiva/disattiva le Opzioni e la Galleria" "title": "Attiva/disattiva le Opzioni e la Galleria"
}, },
"clearSearch": "Cancella ricerca" "clearSearch": "Cancella ricerca",
"remixImage": {
"desc": "Utilizza tutti i parametri tranne il seme dell'immagine corrente",
"title": "Remixa l'immagine"
}
}, },
"modelManager": { "modelManager": {
"modelManager": "Gestione Modelli", "modelManager": "Gestione Modelli",
@ -670,7 +674,8 @@
"aspect": "Aspetto", "aspect": "Aspetto",
"setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (potrebbe essere troppo grande)", "setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (potrebbe essere troppo grande)",
"boxBlur": "Box", "boxBlur": "Box",
"gaussianBlur": "Gaussian" "gaussianBlur": "Gaussian",
"remixImage": "Remixa l'immagine"
}, },
"settings": { "settings": {
"models": "Modelli", "models": "Modelli",
@ -1245,7 +1250,11 @@
"scribble": "Scarabocchio", "scribble": "Scarabocchio",
"amult": "Angolo di illuminazione", "amult": "Angolo di illuminazione",
"coarse": "Approssimativo", "coarse": "Approssimativo",
"resizeSimple": "Ridimensiona (semplice)" "resizeSimple": "Ridimensiona (semplice)",
"large": "Grande",
"small": "Piccolo",
"depthAnythingDescription": "Generazione di mappe di profondità utilizzando la tecnica Depth Anything",
"modelSize": "Dimensioni del modello"
}, },
"queue": { "queue": {
"queueFront": "Aggiungi all'inizio della coda", "queueFront": "Aggiungi all'inizio della coda",
@ -1677,7 +1686,9 @@
"userWorkflows": "I miei flussi di lavoro", "userWorkflows": "I miei flussi di lavoro",
"newWorkflowCreated": "Nuovo flusso di lavoro creato", "newWorkflowCreated": "Nuovo flusso di lavoro creato",
"downloadWorkflow": "Salva su file", "downloadWorkflow": "Salva su file",
"uploadWorkflow": "Carica da file" "uploadWorkflow": "Carica da file",
"projectWorkflows": "Flussi di lavoro del progetto",
"noWorkflows": "Nessun flusso di lavoro"
}, },
"app": { "app": {
"storeNotInitialized": "Il negozio non è inizializzato" "storeNotInitialized": "Il negozio non è inizializzato"

View File

@ -2,29 +2,35 @@
"accessibility": { "accessibility": {
"invokeProgressBar": "Invoke ilerleme durumu", "invokeProgressBar": "Invoke ilerleme durumu",
"nextImage": "Sonraki Resim", "nextImage": "Sonraki Resim",
"useThisParameter": "Kullanıcı parametreleri", "useThisParameter": "Bu ayarları kullan",
"copyMetadataJson": "Metadata verilerini kopyala (JSON)", "copyMetadataJson": "Metadata verilerini kopyala (JSON)",
"exitViewer": "Görüntüleme Modundan Çık", "exitViewer": "Görüntüleme Modundan Çık",
"zoomIn": "Yakınlaştır", "zoomIn": "Yakınlaştır",
"zoomOut": "Uzaklaştır", "zoomOut": "Uzaklaştır",
"rotateCounterClockwise": "Döndür (Saat yönünün tersine)", "rotateCounterClockwise": "Saat yönünün tersine döndür",
"rotateClockwise": "Döndür (Saat yönünde)", "rotateClockwise": "Saat yönüne döndür",
"flipHorizontally": "Yatay Çevir", "flipHorizontally": "Yatay Çevir",
"flipVertically": "Dikey Çevir", "flipVertically": "Dikey Çevir",
"modifyConfig": "Ayarları Değiştir", "modifyConfig": "Ayarları Değiştir",
"toggleAutoscroll": "Otomatik kaydırmayı/kapat", "toggleAutoscroll": "Otomatik kaydırmayı-kapa",
"toggleLogViewer": "Günlük Görüntüleyici Aç/Kapa", "toggleLogViewer": "Günlüğü Aç-Kapa",
"showOptionsPanel": "Ayarlar Panelini Göster", "showOptionsPanel": "Yan Paneli Göster",
"modelSelect": "Model Seçin", "modelSelect": "Model Seçimi",
"reset": "Sıfırla", "reset": "Sıfırla",
"uploadImage": "Resim Yükle", "uploadImage": "Resim Yükle",
"previousImage": "Önceki Resim", "previousImage": "Önceki Resim",
"menu": "Menü" "menu": "Menü",
"about": "Hakkında",
"mode": "Kip",
"resetUI": "$t(accessibility.reset)Arayüz",
"showGalleryPanel": "Galeri Panelini Göster",
"loadMore": "Daha Getir",
"createIssue": "Sorun Bildir"
}, },
"common": { "common": {
"hotkeysLabel": "Kısayol Tuşları", "hotkeysLabel": "Kısayol Tuşları",
"languagePickerLabel": "Dil Seçimi", "languagePickerLabel": "Dil",
"reportBugLabel": "Hata Bildir", "reportBugLabel": "Sorun Bildir",
"githubLabel": "Github", "githubLabel": "Github",
"discordLabel": "Discord", "discordLabel": "Discord",
"settingsLabel": "Ayarlar", "settingsLabel": "Ayarlar",
@ -37,22 +43,128 @@
"langJapanese": "Japonca", "langJapanese": "Japonca",
"langPolish": "Lehçe", "langPolish": "Lehçe",
"langPortuguese": "Portekizce", "langPortuguese": "Portekizce",
"langBrPortuguese": "Portekizcr (Brezilya)", "langBrPortuguese": "Portekizce (Brezilya)",
"langRussian": "Rusça", "langRussian": "Rusça",
"langSimplifiedChinese": "Çince (Basit)", "langSimplifiedChinese": "Çince (Basit)",
"langUkranian": "Ukraynaca", "langUkranian": "Ukraynaca",
"langSpanish": "İspanyolca", "langSpanish": "İspanyolca",
"txt2img": "Metinden Resime", "txt2img": "Yazıdan Resime",
"img2img": "Resimden Metine", "img2img": "Resimden Resime",
"linear": "Çizgisel", "linear": "Doğrusal",
"nodes": "Düğümler", "nodes": "İş Akış Düzenleyici",
"postprocessing": "İşlem Sonrası", "postprocessing": "Rötuş",
"postProcessing": "İşlem Sonrası", "postProcessing": "Rötuş",
"postProcessDesc2": "Daha gelişmiş özellikler için ve iş akışını kolaylaştırmak için özel bir kullanıcı arayüzü çok yakında yayınlanacaktır.", "postProcessDesc2": "Daha gelişmiş iş akışlarına olanak sağlayacak özel bir arayüz yakında yayınlanacaktır.",
"postProcessDesc3": "Invoke AI komut satırı arayüzü, bir çok yeni özellik sunmaktadır.", "postProcessDesc3": "Invoke AI Komut Satırı Arayüzü, Embiggen dahil birçok yeni özellik sunmaktadır.",
"langKorean": "Korece", "langKorean": "Korece",
"unifiedCanvas": "Akıllı Tuval", "unifiedCanvas": "Akıllı Tuval",
"nodesDesc": "Görüntülerin oluşturulmasında hazırladığımız yeni bir sistem geliştirme aşamasındadır. Bu harika özellikler ve çok daha fazlası için bizi takip etmeye devam edin.", "nodesDesc": "Resim oluşturmak için hazırladığımız çizge tabanlı sistem şu an geliştirme aşamasındadır. Bu harika özellik hakkındaki gelişmeler için bizi takip etmeye devam edin.",
"postProcessDesc1": "Invoke AI son kullanıcıya yönelik bir çok özellik sunar. Görüntü kalitesi yükseltme, yüz restorasyonu WebUI üzerinden kullanılabilir. Metinden resime ve resimden metne araçlarına gelişmiş seçenekler menüsünden ulaşabilirsiniz. İsterseniz mevcut görüntü ekranının üzerindeki veya görüntüleyicideki görüntüyü doğrudan düzenleyebilirsiniz." "postProcessDesc1": "Invoke AI birçok rötuş (post-process) aracı sağlar. Resim büyütme ve yüz iyileştirme halihazırda WebUI üzerinden kullanılabilir. Bunlara Yazıdan Resime ve Resimden Resime sekmelerindeki Gelişmiş Ayarlar menüsünden ulaşabilirsiniz. İsterseniz mevcut görüntü ekranının üzerindeki veya görüntüleyicideki resmi doğrudan üstteki tuşlar yardımıyla düzenleyebilirsiniz.",
"batch": "Toplu İş Yöneticisi",
"accept": "Kabul et",
"cancel": "İptal et",
"advanced": "Gelişmiş",
"copyError": "$t(gallery.copy) Hata",
"on": "Açık",
"or": "ya da",
"aboutDesc": "Invoke'u iş için mi kullanıyorsunuz? Şuna bir göz atın:",
"advancedOptions": "Gelişmiş Ayarlar",
"ai": "yapay zeka",
"close": "Kapat",
"auto": "Otomatik",
"communityLabel": "Topluluk",
"back": "Geri",
"areYouSure": "Emin misiniz?",
"notInstalled": "$t(common.installed) Değil",
"openInNewTab": "Yeni Sekmede Aç",
"aboutHeading": "Yaratıcı Gücünüzün Sahibi Olun",
"lightMode": "Açık Tema",
"load": "Yükle",
"loading": "Yükleniyor",
"loadingInvokeAI": "Invoke AI Yükleniyor",
"localSystem": "Yerel Sistem",
"inpaint": "içboyama",
"modelManager": "Model Yöneticisi",
"orderBy": "Sırala",
"outpaint": "dışboyama",
"outputs": ıktılar",
"langHebrew": "İbranice",
"learnMore": "Bilgi Edin",
"nodeEditor": "Çizge Düzenleyici",
"save": "Kaydet",
"statusMergingModels": "Modeller Birleştiriliyor",
"statusGenerating": "Oluşturuluyor",
"statusGenerationComplete": "Oluşturma Tamamlandı",
"statusGeneratingOutpainting": "Dışboyama Oluşturuluyor",
"statusLoadingModel": "Model Yükleniyor",
"random": "Rastgele",
"simple": "Basit",
"preferencesLabel": "Seçenekler",
"statusConnected": "Bağlandı",
"statusMergedModels": "Modeller Birleştirildi",
"statusModelChanged": "Model Değişti",
"statusModelConverted": "Model Dönüştürüldü",
"statusPreparing": "Hazırlanıyor",
"statusProcessing": "İşleniyor",
"statusProcessingCanceled": "İşlem İptal Edildi",
"statusRestoringFacesCodeFormer": "Yüzler İyileştiriliyor (CodeFormer)",
"statusRestoringFacesGFPGAN": "Yüzler İyileştiriliyor (GFPGAN)",
"template": "Şablon",
"saveAs": "Farklı Kaydet",
"statusProcessingComplete": "İşlem Tamamlandı",
"statusSavingImage": "Resim Kaydediliyor",
"somethingWentWrong": "Bir sorun oluştu",
"statusConvertingModel": "Model Dönüştürülüyor",
"statusDisconnected": "Bağlantı Kesildi",
"statusError": "Hata",
"statusGeneratingImageToImage": "Resimden Resim Oluşturuluyor",
"statusGeneratingInpainting": "İçboyama Oluşturuluyor",
"statusRestoringFaces": "Yüzler İyileştiriliyor",
"statusUpscaling": "Büyütme",
"statusUpscalingESRGAN": "Büyütme (ESRGAN)",
"training": "Eğitim",
"statusGeneratingTextToImage": "Yazıdan Resim Oluşturuluyor"
},
"accordions": {
"generation": {
"title": "Oluşturma",
"modelTab": "Model",
"conceptsTab": "Konseptler"
},
"image": {
"title": "Resim"
},
"advanced": {
"title": "Gelişmiş"
},
"compositing": {
"title": "Birleştirme",
"coherenceTab": "Uyum Geçişi",
"infillTab": "Doldurma"
}
},
"boards": {
"autoAddBoard": "Panoya Otomatik Ekleme",
"cancel": "İptal et",
"clearSearch": "Aramayı Sıfırla",
"deleteBoard": "Panoyu Sil",
"loading": "Yükleniyor...",
"myBoard": "Panom",
"selectBoard": "Bir Pano Seç",
"addBoard": "Pano Ekle",
"deleteBoardAndImages": "Panoyu ve Resimleri Sil",
"deleteBoardOnly": "Sadece Panoyu Sil",
"deletedBoardsCannotbeRestored": "Silinen panolar geri getirilemez",
"menuItemAutoAdd": "Bu panoya otomatik olarak ekle",
"move": "Taşı",
"movingImagesToBoard_one": "{{count}} resmi şu panoya taşı:",
"movingImagesToBoard_other": "{{count}} resmi şu panoya taşı:",
"noMatching": "Eşleşen pano yok",
"searchBoard": "Pano Ara...",
"topMessage": "Bu pano, şu özelliklerde kullanılan resimler içeriyor:",
"downloadBoard": "Panoyu İndir",
"uncategorized": "Kategorisiz",
"changeBoard": "Panoyu Değiştir",
"bottomMessage": "Bu panoyu ve resimlerini silmek, bunları kullanan özelliklerin sıfırlanmasına neden olacaktır."
} }
} }

View File

@ -6,9 +6,7 @@ const OPENAPI_URL = 'http://127.0.0.1:9090/openapi.json';
const OUTPUT_FILE = 'src/services/api/schema.ts'; const OUTPUT_FILE = 'src/services/api/schema.ts';
async function main() { async function main() {
process.stdout.write( process.stdout.write(`Generating types "${OPENAPI_URL}" --> "${OUTPUT_FILE}"...`);
`Generating types "${OPENAPI_URL}" --> "${OUTPUT_FILE}"...`
);
const types = await openapiTS(OPENAPI_URL, { const types = await openapiTS(OPENAPI_URL, {
exportType: true, exportType: true,
transform: (schemaObject) => { transform: (schemaObject) => {

View File

@ -1,4 +1,4 @@
import { Box, useGlobalModifiersInit } from '@invoke-ai/ui'; import { Box, useGlobalModifiersInit } from '@invoke-ai/ui-library';
import { useSocketIO } from 'app/hooks/useSocketIO'; import { useSocketIO } from 'app/hooks/useSocketIO';
import { useLogger } from 'app/logging/useLogger'; import { useLogger } from 'app/logging/useLogger';
import { appStarted } from 'app/store/middleware/listenerMiddleware/listeners/appStarted'; import { appStarted } from 'app/store/middleware/listenerMiddleware/listeners/appStarted';
@ -45,8 +45,7 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage }: Props) => {
useGlobalModifiersInit(); useGlobalModifiersInit();
useGlobalHotkeys(); useGlobalHotkeys();
const { dropzone, isHandlingUpload, setIsHandlingUpload } = const { dropzone, isHandlingUpload, setIsHandlingUpload } = useFullscreenDropzone();
useFullscreenDropzone();
const handleReset = useCallback(() => { const handleReset = useCallback(() => {
clearStorage(); clearStorage();
@ -70,10 +69,7 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage }: Props) => {
}, [dispatch]); }, [dispatch]);
return ( return (
<ErrorBoundary <ErrorBoundary onReset={handleReset} FallbackComponent={AppErrorBoundaryFallback}>
onReset={handleReset}
FallbackComponent={AppErrorBoundaryFallback}
>
<Box <Box
id="invoke-app-wrapper" id="invoke-app-wrapper"
w="100vw" w="100vw"
@ -86,10 +82,7 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage }: Props) => {
<InvokeTabs /> <InvokeTabs />
<AnimatePresence> <AnimatePresence>
{dropzone.isDragActive && isHandlingUpload && ( {dropzone.isDragActive && isHandlingUpload && (
<ImageUploadOverlay <ImageUploadOverlay dropzone={dropzone} setIsHandlingUpload={setIsHandlingUpload} />
dropzone={dropzone}
setIsHandlingUpload={setIsHandlingUpload}
/>
)} )}
</AnimatePresence> </AnimatePresence>
</Box> </Box>

View File

@ -1,12 +1,8 @@
import { Button, Flex, Heading, Link, Text, useToast } from '@invoke-ai/ui'; import { Button, Flex, Heading, Link, Text, useToast } from '@invoke-ai/ui-library';
import newGithubIssueUrl from 'new-github-issue-url'; import newGithubIssueUrl from 'new-github-issue-url';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import { PiArrowCounterClockwiseBold, PiArrowSquareOutBold, PiCopyBold } from 'react-icons/pi';
PiArrowCounterClockwiseBold,
PiArrowSquareOutBold,
PiCopyBold,
} from 'react-icons/pi';
import { serializeError } from 'serialize-error'; import { serializeError } from 'serialize-error';
type Props = { type Props = {
@ -37,22 +33,8 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
[error.message, error.name] [error.message, error.name]
); );
return ( return (
<Flex <Flex layerStyle="body" w="100vw" h="100vh" alignItems="center" justifyContent="center" p={4}>
layerStyle="body" <Flex layerStyle="first" flexDir="column" borderRadius="base" justifyContent="center" gap={8} p={16}>
w="100vw"
h="100vh"
alignItems="center"
justifyContent="center"
p={4}
>
<Flex
layerStyle="first"
flexDir="column"
borderRadius="base"
justifyContent="center"
gap={8}
p={16}
>
<Heading>{t('common.somethingWentWrong')}</Heading> <Heading>{t('common.somethingWentWrong')}</Heading>
<Flex <Flex
layerStyle="second" layerStyle="second"
@ -68,19 +50,14 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
</Text> </Text>
</Flex> </Flex>
<Flex gap={4}> <Flex gap={4}>
<Button <Button leftIcon={<PiArrowCounterClockwiseBold />} onClick={resetErrorBoundary}>
leftIcon={<PiArrowCounterClockwiseBold />}
onClick={resetErrorBoundary}
>
{t('accessibility.resetUI')} {t('accessibility.resetUI')}
</Button> </Button>
<Button leftIcon={<PiCopyBold />} onClick={handleCopy}> <Button leftIcon={<PiCopyBold />} onClick={handleCopy}>
{t('common.copyError')} {t('common.copyError')}
</Button> </Button>
<Link href={url} isExternal> <Link href={url} isExternal>
<Button leftIcon={<PiArrowSquareOutBold />}> <Button leftIcon={<PiArrowSquareOutBold />}>{t('accessibility.createIssue')}</Button>
{t('accessibility.createIssue')}
</Button>
</Link> </Link>
</Flex> </Flex>
</Flex> </Flex>

View File

@ -1,13 +1,7 @@
import '@fontsource-variable/inter'; import '@fontsource-variable/inter';
import 'overlayscrollbars/overlayscrollbars.css'; import 'overlayscrollbars/overlayscrollbars.css';
import { import { ChakraProvider, DarkMode, extendTheme, theme as _theme, TOAST_OPTIONS } from '@invoke-ai/ui-library';
ChakraProvider,
DarkMode,
extendTheme,
theme as _theme,
TOAST_OPTIONS,
} from '@invoke-ai/ui';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { memo, useEffect, useMemo } from 'react'; import { memo, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';

View File

@ -1,4 +1,4 @@
import { useToast } from '@invoke-ai/ui'; import { useToast } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { addToast, clearToastQueue } from 'features/system/store/systemSlice'; import { addToast, clearToastQueue } from 'features/system/store/systemSlice';
import type { MakeToastArg } from 'features/system/util/makeToast'; import type { MakeToastArg } from 'features/system/util/makeToast';
@ -36,10 +36,7 @@ const Toaster = () => {
*/ */
export const useAppToaster = () => { export const useAppToaster = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const toaster = useCallback( const toaster = useCallback((arg: MakeToastArg) => dispatch(addToast(makeToast(arg))), [dispatch]);
(arg: MakeToastArg) => dispatch(addToast(makeToast(arg))),
[dispatch]
);
return toaster; return toaster;
}; };

View File

@ -6,10 +6,7 @@ import { useAppDispatch } from 'app/store/storeHooks';
import type { MapStore } from 'nanostores'; import type { MapStore } from 'nanostores';
import { atom, map } from 'nanostores'; import { atom, map } from 'nanostores';
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import type { import type { ClientToServerEvents, ServerToClientEvents } from 'services/events/types';
ClientToServerEvents,
ServerToClientEvents,
} from 'services/events/types';
import { setEventListeners } from 'services/events/util/setEventListeners'; import { setEventListeners } from 'services/events/util/setEventListeners';
import type { ManagerOptions, Socket, SocketOptions } from 'socket.io-client'; import type { ManagerOptions, Socket, SocketOptions } from 'socket.io-client';
import { io } from 'socket.io-client'; import { io } from 'socket.io-client';
@ -45,9 +42,7 @@ export const useSocketIO = () => {
const socketOptions = useMemo(() => { const socketOptions = useMemo(() => {
const options: Partial<ManagerOptions & SocketOptions> = { const options: Partial<ManagerOptions & SocketOptions> = {
timeout: 60000, timeout: 60000,
path: baseUrl path: baseUrl ? '/ws/socket.io' : `${window.location.pathname}ws/socket.io`,
? '/ws/socket.io'
: `${window.location.pathname}ws/socket.io`,
autoConnect: false, // achtung! removing this breaks the dynamic middleware autoConnect: false, // achtung! removing this breaks the dynamic middleware
forceNew: true, forceNew: true,
}; };
@ -66,10 +61,7 @@ export const useSocketIO = () => {
return; return;
} }
const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io( const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io(socketUrl, socketOptions);
socketUrl,
socketOptions
);
setEventListeners({ dispatch, socket }); setEventListeners({ dispatch, socket });
socket.connect(); socket.connect();

View File

@ -30,20 +30,11 @@ export type LoggerNamespace =
| 'queue' | 'queue'
| 'dnd'; | 'dnd';
export const logger = (namespace: LoggerNamespace) => export const logger = (namespace: LoggerNamespace) => $logger.get().child({ namespace });
$logger.get().child({ namespace });
export const zLogLevel = z.enum([ export const zLogLevel = z.enum(['trace', 'debug', 'info', 'warn', 'error', 'fatal']);
'trace',
'debug',
'info',
'warn',
'error',
'fatal',
]);
export type LogLevel = z.infer<typeof zLogLevel>; export type LogLevel = z.infer<typeof zLogLevel>;
export const isLogLevel = (v: unknown): v is LogLevel => export const isLogLevel = (v: unknown): v is LogLevel => zLogLevel.safeParse(v).success;
zLogLevel.safeParse(v).success;
// Translate human-readable log levels to numbers, used for log filtering // Translate human-readable log levels to numbers, used for log filtering
export const LOG_LEVEL_MAP: Record<LogLevel, number> = { export const LOG_LEVEL_MAP: Record<LogLevel, number> = {

View File

@ -17,10 +17,7 @@ export const useLogger = (namespace: LoggerNamespace) => {
localStorage.setItem('ROARR_LOG', 'true'); localStorage.setItem('ROARR_LOG', 'true');
// Use a filter to show only logs of the given level // Use a filter to show only logs of the given level
localStorage.setItem( localStorage.setItem('ROARR_FILTER', `context.logLevel:>=${LOG_LEVEL_MAP[consoleLogLevel]}`);
'ROARR_FILTER',
`context.logLevel:>=${LOG_LEVEL_MAP[consoleLogLevel]}`
);
} else { } else {
// Disable console log output // Disable console log output
localStorage.setItem('ROARR_LOG', 'false'); localStorage.setItem('ROARR_LOG', 'false');

View File

@ -1,8 +1,4 @@
import { import { createDraftSafeSelectorCreator, createSelectorCreator, lruMemoize } from '@reduxjs/toolkit';
createDraftSafeSelectorCreator,
createSelectorCreator,
lruMemoize,
} from '@reduxjs/toolkit';
import type { GetSelectorsOptions } from '@reduxjs/toolkit/dist/entities/state_selectors'; import type { GetSelectorsOptions } from '@reduxjs/toolkit/dist/entities/state_selectors';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';

View File

@ -1,19 +1,12 @@
import { StorageError } from 'app/store/enhancers/reduxRemember/errors'; import { StorageError } from 'app/store/enhancers/reduxRemember/errors';
import { $projectId } from 'app/store/nanostores/projectId'; import { $projectId } from 'app/store/nanostores/projectId';
import type { UseStore } from 'idb-keyval'; import type { UseStore } from 'idb-keyval';
import { import { clear, createStore as createIDBKeyValStore, get, set } from 'idb-keyval';
clear,
createStore as createIDBKeyValStore,
get,
set,
} from 'idb-keyval';
import { action, atom } from 'nanostores'; import { action, atom } from 'nanostores';
import type { Driver } from 'redux-remember'; import type { Driver } from 'redux-remember';
// Create a custom idb-keyval store (just needed to customize the name) // Create a custom idb-keyval store (just needed to customize the name)
export const $idbKeyValStore = atom<UseStore>( export const $idbKeyValStore = atom<UseStore>(createIDBKeyValStore('invoke', 'invoke-store'));
createIDBKeyValStore('invoke', 'invoke-store')
);
export const clearIdbKeyValStore = action($idbKeyValStore, 'clear', (store) => { export const clearIdbKeyValStore = action($idbKeyValStore, 'clear', (store) => {
clear(store.get()); clear(store.get());

View File

@ -4,13 +4,12 @@ import { diff } from 'jsondiffpatch';
/** /**
* Super simple logger middleware. Useful for debugging when the redux devtools are awkward. * Super simple logger middleware. Useful for debugging when the redux devtools are awkward.
*/ */
export const debugLoggerMiddleware: Middleware = export const debugLoggerMiddleware: Middleware = (api: MiddlewareAPI) => (next) => (action) => {
(api: MiddlewareAPI) => (next) => (action) => { const originalState = api.getState();
const originalState = api.getState(); console.log('REDUX: dispatching', action);
console.log('REDUX: dispatching', action); const result = next(action);
const result = next(action); const nextState = api.getState();
const nextState = api.getState(); console.log('REDUX: next state', nextState);
console.log('REDUX: next state', nextState); console.log('REDUX: diff', diff(originalState, nextState));
console.log('REDUX: diff', diff(originalState, nextState)); return result;
return result; };
};

View File

@ -35,8 +35,7 @@ export const actionSanitizer = <A extends UnknownAction>(action: A): A => {
if (socketGeneratorProgress.match(action)) { if (socketGeneratorProgress.match(action)) {
const sanitized = cloneDeep(action); const sanitized = cloneDeep(action);
if (sanitized.payload.data.progress_image) { if (sanitized.payload.data.progress_image) {
sanitized.payload.data.progress_image.dataURL = sanitized.payload.data.progress_image.dataURL = '<Progress image omitted>';
'<Progress image omitted>';
} }
return sanitized; return sanitized;
} }

View File

@ -1,9 +1,4 @@
import type { import type { ListenerEffect, TypedAddListener, TypedStartListening, UnknownAction } from '@reduxjs/toolkit';
ListenerEffect,
TypedAddListener,
TypedStartListening,
UnknownAction,
} from '@reduxjs/toolkit';
import { addListener, createListenerMiddleware } from '@reduxjs/toolkit'; import { addListener, createListenerMiddleware } from '@reduxjs/toolkit';
import { addGalleryImageClickedListener } from 'app/store/middleware/listenerMiddleware/listeners/galleryImageClicked'; import { addGalleryImageClickedListener } from 'app/store/middleware/listenerMiddleware/listeners/galleryImageClicked';
import type { AppDispatch, RootState } from 'app/store/store'; import type { AppDispatch, RootState } from 'app/store/store';
@ -47,10 +42,7 @@ import {
import { addImagesStarredListener } from './listeners/imagesStarred'; import { addImagesStarredListener } from './listeners/imagesStarred';
import { addImagesUnstarredListener } from './listeners/imagesUnstarred'; import { addImagesUnstarredListener } from './listeners/imagesUnstarred';
import { addImageToDeleteSelectedListener } from './listeners/imageToDeleteSelected'; import { addImageToDeleteSelectedListener } from './listeners/imageToDeleteSelected';
import { import { addImageUploadedFulfilledListener, addImageUploadedRejectedListener } from './listeners/imageUploaded';
addImageUploadedFulfilledListener,
addImageUploadedRejectedListener,
} from './listeners/imageUploaded';
import { addInitialImageSelectedListener } from './listeners/initialImageSelected'; import { addInitialImageSelectedListener } from './listeners/initialImageSelected';
import { addModelSelectedListener } from './listeners/modelSelected'; import { addModelSelectedListener } from './listeners/modelSelected';
import { addModelsLoadedListener } from './listeners/modelsLoaded'; import { addModelsLoadedListener } from './listeners/modelsLoaded';
@ -78,19 +70,11 @@ export const listenerMiddleware = createListenerMiddleware();
export type AppStartListening = TypedStartListening<RootState, AppDispatch>; export type AppStartListening = TypedStartListening<RootState, AppDispatch>;
export const startAppListening = export const startAppListening = listenerMiddleware.startListening as AppStartListening;
listenerMiddleware.startListening as AppStartListening;
export const addAppListener = addListener as TypedAddListener< export const addAppListener = addListener as TypedAddListener<RootState, AppDispatch>;
RootState,
AppDispatch
>;
export type AppListenerEffect = ListenerEffect< export type AppListenerEffect = ListenerEffect<UnknownAction, RootState, AppDispatch>;
UnknownAction,
RootState,
AppDispatch
>;
/** /**
* The RTK listener middleware is a lightweight alternative sagas/observables. * The RTK listener middleware is a lightweight alternative sagas/observables.

View File

@ -1,10 +1,6 @@
import { isAnyOf } from '@reduxjs/toolkit'; import { isAnyOf } from '@reduxjs/toolkit';
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import { import { canvasBatchIdsReset, commitStagingAreaImage, discardStagedImages } from 'features/canvas/store/canvasSlice';
canvasBatchIdsReset,
commitStagingAreaImage,
discardStagedImages,
} from 'features/canvas/store/canvasSlice';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next'; import { t } from 'i18next';
import { queueApi } from 'services/api/endpoints/queue'; import { queueApi } from 'services/api/endpoints/queue';
@ -23,10 +19,7 @@ export const addCommitStagingAreaImageListener = () => {
try { try {
const req = dispatch( const req = dispatch(
queueApi.endpoints.cancelByBatchIds.initiate( queueApi.endpoints.cancelByBatchIds.initiate({ batch_ids: batchIds }, { fixedCacheKey: 'cancelByBatchIds' })
{ batch_ids: batchIds },
{ fixedCacheKey: 'cancelByBatchIds' }
)
); );
const { canceled } = await req.unwrap(); const { canceled } = await req.unwrap();
req.reset(); req.reset();

View File

@ -12,15 +12,9 @@ export const appStarted = createAction('app/appStarted');
export const addFirstListImagesListener = () => { export const addFirstListImagesListener = () => {
startAppListening({ startAppListening({
matcher: imagesApi.endpoints.listImages.matchFulfilled, matcher: imagesApi.endpoints.listImages.matchFulfilled,
effect: async ( effect: async (action, { dispatch, unsubscribe, cancelActiveListeners }) => {
action,
{ dispatch, unsubscribe, cancelActiveListeners }
) => {
// Only run this listener on the first listImages request for no-board images // Only run this listener on the first listImages request for no-board images
if ( if (action.meta.arg.queryCacheKey !== getListImagesUrl({ board_id: 'none', categories: IMAGE_CATEGORIES })) {
action.meta.arg.queryCacheKey !==
getListImagesUrl({ board_id: 'none', categories: IMAGE_CATEGORIES })
) {
return; return;
} }

View File

@ -1,8 +1,5 @@
import { setInfillMethod } from 'features/parameters/store/generationSlice'; import { setInfillMethod } from 'features/parameters/store/generationSlice';
import { import { shouldUseNSFWCheckerChanged, shouldUseWatermarkerChanged } from 'features/system/store/systemSlice';
shouldUseNSFWCheckerChanged,
shouldUseWatermarkerChanged,
} from 'features/system/store/systemSlice';
import { appInfoApi } from 'services/api/endpoints/appInfo'; import { appInfoApi } from 'services/api/endpoints/appInfo';
import { startAppListening } from '..'; import { startAppListening } from '..';
@ -11,11 +8,7 @@ export const addAppConfigReceivedListener = () => {
startAppListening({ startAppListening({
matcher: appInfoApi.endpoints.getAppConfig.matchFulfilled, matcher: appInfoApi.endpoints.getAppConfig.matchFulfilled,
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
const { const { infill_methods = [], nsfw_methods = [], watermarking_methods = [] } = action.payload;
infill_methods = [],
nsfw_methods = [],
watermarking_methods = [],
} = action.payload;
const infillMethod = getState().generation.infillMethod; const infillMethod = getState().generation.infillMethod;
if (!infill_methods.includes(infillMethod)) { if (!infill_methods.includes(infillMethod)) {

View File

@ -1,4 +1,4 @@
import { createStandaloneToast, theme, TOAST_OPTIONS } from '@invoke-ai/ui'; import { createStandaloneToast, theme, TOAST_OPTIONS } from '@invoke-ai/ui-library';
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize'; import { parseify } from 'common/util/serialize';
import { zPydanticValidationError } from 'features/system/store/zodSchemas'; import { zPydanticValidationError } from 'features/system/store/zodSchemas';
@ -20,10 +20,7 @@ export const addBatchEnqueuedListener = () => {
effect: async (action) => { effect: async (action) => {
const response = action.payload; const response = action.payload;
const arg = action.meta.arg.originalArgs; const arg = action.meta.arg.originalArgs;
logger('queue').debug( logger('queue').debug({ enqueueResult: parseify(response) }, 'Batch enqueued');
{ enqueueResult: parseify(response) },
'Batch enqueued'
);
if (!toast.isActive('batch-queued')) { if (!toast.isActive('batch-queued')) {
toast({ toast({
@ -53,10 +50,7 @@ export const addBatchEnqueuedListener = () => {
status: 'error', status: 'error',
description: 'Unknown Error', description: 'Unknown Error',
}); });
logger('queue').error( logger('queue').error({ batchConfig: parseify(arg), error: parseify(response) }, t('queue.batchFailedToQueue'));
{ batchConfig: parseify(arg), error: parseify(response) },
t('queue.batchFailedToQueue')
);
return; return;
} }
@ -81,10 +75,7 @@ export const addBatchEnqueuedListener = () => {
status: 'error', status: 'error',
}); });
} }
logger('queue').error( logger('queue').error({ batchConfig: parseify(arg), error: parseify(response) }, t('queue.batchFailedToQueue'));
{ batchConfig: parseify(arg), error: parseify(response) },
t('queue.batchFailedToQueue')
);
}, },
}); });
}; };

View File

@ -22,13 +22,7 @@ export const addDeleteBoardAndImagesFulfilledListener = () => {
const { generation, canvas, nodes, controlAdapters } = getState(); const { generation, canvas, nodes, controlAdapters } = getState();
deleted_images.forEach((image_name) => { deleted_images.forEach((image_name) => {
const imageUsage = getImageUsage( const imageUsage = getImageUsage(generation, canvas, nodes, controlAdapters, image_name);
generation,
canvas,
nodes,
controlAdapters,
image_name
);
if (imageUsage.isInitialImage && !wasInitialImageReset) { if (imageUsage.isInitialImage && !wasInitialImageReset) {
dispatch(clearInitialImage()); dispatch(clearInitialImage());

View File

@ -1,13 +1,6 @@
import { isAnyOf } from '@reduxjs/toolkit'; import { isAnyOf } from '@reduxjs/toolkit';
import { import { boardIdSelected, galleryViewChanged, imageSelected } from 'features/gallery/store/gallerySlice';
boardIdSelected, import { ASSETS_CATEGORIES, IMAGE_CATEGORIES } from 'features/gallery/store/types';
galleryViewChanged,
imageSelected,
} from 'features/gallery/store/gallerySlice';
import {
ASSETS_CATEGORIES,
IMAGE_CATEGORIES,
} from 'features/gallery/store/types';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { imagesSelectors } from 'services/api/util'; import { imagesSelectors } from 'services/api/util';
@ -16,53 +9,35 @@ import { startAppListening } from '..';
export const addBoardIdSelectedListener = () => { export const addBoardIdSelectedListener = () => {
startAppListening({ startAppListening({
matcher: isAnyOf(boardIdSelected, galleryViewChanged), matcher: isAnyOf(boardIdSelected, galleryViewChanged),
effect: async ( effect: async (action, { getState, dispatch, condition, cancelActiveListeners }) => {
action,
{ getState, dispatch, condition, cancelActiveListeners }
) => {
// Cancel any in-progress instances of this listener, we don't want to select an image from a previous board // Cancel any in-progress instances of this listener, we don't want to select an image from a previous board
cancelActiveListeners(); cancelActiveListeners();
const state = getState(); const state = getState();
const board_id = boardIdSelected.match(action) const board_id = boardIdSelected.match(action) ? action.payload.boardId : state.gallery.selectedBoardId;
? action.payload.boardId
: state.gallery.selectedBoardId;
const galleryView = galleryViewChanged.match(action) const galleryView = galleryViewChanged.match(action) ? action.payload : state.gallery.galleryView;
? action.payload
: state.gallery.galleryView;
// when a board is selected, we need to wait until the board has loaded *some* images, then select the first one // when a board is selected, we need to wait until the board has loaded *some* images, then select the first one
const categories = const categories = galleryView === 'images' ? IMAGE_CATEGORIES : ASSETS_CATEGORIES;
galleryView === 'images' ? IMAGE_CATEGORIES : ASSETS_CATEGORIES;
const queryArgs = { board_id: board_id ?? 'none', categories }; const queryArgs = { board_id: board_id ?? 'none', categories };
// wait until the board has some images - maybe it already has some from a previous fetch // wait until the board has some images - maybe it already has some from a previous fetch
// must use getState() to ensure we do not have stale state // must use getState() to ensure we do not have stale state
const isSuccess = await condition( const isSuccess = await condition(
() => () => imagesApi.endpoints.listImages.select(queryArgs)(getState()).isSuccess,
imagesApi.endpoints.listImages.select(queryArgs)(getState())
.isSuccess,
5000 5000
); );
if (isSuccess) { if (isSuccess) {
// the board was just changed - we can select the first image // the board was just changed - we can select the first image
const { data: boardImagesData } = const { data: boardImagesData } = imagesApi.endpoints.listImages.select(queryArgs)(getState());
imagesApi.endpoints.listImages.select(queryArgs)(getState());
if ( if (boardImagesData && boardIdSelected.match(action) && action.payload.selectedImageName) {
boardImagesData &&
boardIdSelected.match(action) &&
action.payload.selectedImageName
) {
const firstImage = imagesSelectors.selectAll(boardImagesData)[0]; const firstImage = imagesSelectors.selectAll(boardImagesData)[0];
const selectedImage = imagesSelectors.selectById( const selectedImage = imagesSelectors.selectById(boardImagesData, action.payload.selectedImageName);
boardImagesData,
action.payload.selectedImageName
);
dispatch(imageSelected(selectedImage || firstImage || null)); dispatch(imageSelected(selectedImage || firstImage || null));
} else { } else {

View File

@ -11,9 +11,7 @@ export const addCanvasCopiedToClipboardListener = () => {
startAppListening({ startAppListening({
actionCreator: canvasCopiedToClipboard, actionCreator: canvasCopiedToClipboard,
effect: async (action, { dispatch, getState }) => { effect: async (action, { dispatch, getState }) => {
const moduleLog = $logger const moduleLog = $logger.get().child({ namespace: 'canvasCopiedToClipboardListener' });
.get()
.child({ namespace: 'canvasCopiedToClipboardListener' });
const state = getState(); const state = getState();
try { try {

View File

@ -11,9 +11,7 @@ export const addCanvasDownloadedAsImageListener = () => {
startAppListening({ startAppListening({
actionCreator: canvasDownloadedAsImage, actionCreator: canvasDownloadedAsImage,
effect: async (action, { dispatch, getState }) => { effect: async (action, { dispatch, getState }) => {
const moduleLog = $logger const moduleLog = $logger.get().child({ namespace: 'canvasSavedToGalleryListener' });
.get()
.child({ namespace: 'canvasSavedToGalleryListener' });
const state = getState(); const state = getState();
let blob; let blob;
@ -32,9 +30,7 @@ export const addCanvasDownloadedAsImageListener = () => {
} }
downloadBlob(blob, 'canvas.png'); downloadBlob(blob, 'canvas.png');
dispatch( dispatch(addToast({ title: t('toast.canvasDownloaded'), status: 'success' }));
addToast({ title: t('toast.canvasDownloaded'), status: 'success' })
);
}, },
}); });
}; };

View File

@ -13,9 +13,7 @@ export const addCanvasMergedListener = () => {
startAppListening({ startAppListening({
actionCreator: canvasMerged, actionCreator: canvasMerged,
effect: async (action, { dispatch }) => { effect: async (action, { dispatch }) => {
const moduleLog = $logger const moduleLog = $logger.get().child({ namespace: 'canvasCopiedToClipboardListener' });
.get()
.child({ namespace: 'canvasCopiedToClipboardListener' });
const blob = await getFullBaseLayerBlob(); const blob = await getFullBaseLayerBlob();
if (!blob) { if (!blob) {

View File

@ -21,11 +21,7 @@ type AnyControlAdapterParamChangeAction =
| ReturnType<typeof controlAdapterProcessortTypeChanged> | ReturnType<typeof controlAdapterProcessortTypeChanged>
| ReturnType<typeof controlAdapterAutoConfigToggled>; | ReturnType<typeof controlAdapterAutoConfigToggled>;
const predicate: AnyListenerPredicate<RootState> = ( const predicate: AnyListenerPredicate<RootState> = (action, state, prevState) => {
action,
state,
prevState
) => {
const isActionMatched = const isActionMatched =
controlAdapterProcessorParamsChanged.match(action) || controlAdapterProcessorParamsChanged.match(action) ||
controlAdapterModelChanged.match(action) || controlAdapterModelChanged.match(action) ||
@ -40,12 +36,7 @@ const predicate: AnyListenerPredicate<RootState> = (
const { id } = action.payload; const { id } = action.payload;
const prevCA = selectControlAdapterById(prevState.controlAdapters, id); const prevCA = selectControlAdapterById(prevState.controlAdapters, id);
const ca = selectControlAdapterById(state.controlAdapters, id); const ca = selectControlAdapterById(state.controlAdapters, id);
if ( if (!prevCA || !isControlNetOrT2IAdapter(prevCA) || !ca || !isControlNetOrT2IAdapter(ca)) {
!prevCA ||
!isControlNetOrT2IAdapter(prevCA) ||
!ca ||
!isControlNetOrT2IAdapter(ca)
) {
return false; return false;
} }

View File

@ -64,37 +64,28 @@ export const addControlNetImageProcessedListener = () => {
); );
const enqueueResult = await req.unwrap(); const enqueueResult = await req.unwrap();
req.reset(); req.reset();
log.debug( log.debug({ enqueueResult: parseify(enqueueResult) }, t('queue.graphQueued'));
{ enqueueResult: parseify(enqueueResult) },
t('queue.graphQueued')
);
const [invocationCompleteAction] = await take( const [invocationCompleteAction] = await take(
(action): action is ReturnType<typeof socketInvocationComplete> => (action): action is ReturnType<typeof socketInvocationComplete> =>
socketInvocationComplete.match(action) && socketInvocationComplete.match(action) &&
action.payload.data.queue_batch_id === action.payload.data.queue_batch_id === enqueueResult.batch.batch_id &&
enqueueResult.batch.batch_id &&
action.payload.data.source_node_id === nodeId action.payload.data.source_node_id === nodeId
); );
// We still have to check the output type // We still have to check the output type
if (isImageOutput(invocationCompleteAction.payload.data.result)) { if (isImageOutput(invocationCompleteAction.payload.data.result)) {
const { image_name } = const { image_name } = invocationCompleteAction.payload.data.result.image;
invocationCompleteAction.payload.data.result.image;
// Wait for the ImageDTO to be received // Wait for the ImageDTO to be received
const [{ payload }] = await take( const [{ payload }] = await take(
(action) => (action) =>
imagesApi.endpoints.getImageDTO.matchFulfilled(action) && imagesApi.endpoints.getImageDTO.matchFulfilled(action) && action.payload.image_name === image_name
action.payload.image_name === image_name
); );
const processedControlImage = payload as ImageDTO; const processedControlImage = payload as ImageDTO;
log.debug( log.debug({ controlNetId: action.payload, processedControlImage }, 'ControlNet image processed');
{ controlNetId: action.payload, processedControlImage },
'ControlNet image processed'
);
// Update the processed image in the store // Update the processed image in the store
dispatch( dispatch(
@ -105,10 +96,7 @@ export const addControlNetImageProcessedListener = () => {
); );
} }
} catch (error) { } catch (error) {
log.error( log.error({ enqueueBatchArg: parseify(enqueueBatchArg) }, t('queue.graphFailedToQueue'));
{ enqueueBatchArg: parseify(enqueueBatchArg) },
t('queue.graphFailedToQueue')
);
if (error instanceof Object) { if (error instanceof Object) {
if ('data' in error && 'status' in error) { if ('data' in error && 'status' in error) {

View File

@ -2,10 +2,7 @@ import { logger } from 'app/logging/logger';
import { enqueueRequested } from 'app/store/actions'; import { enqueueRequested } from 'app/store/actions';
import openBase64ImageInTab from 'common/util/openBase64ImageInTab'; import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
import { parseify } from 'common/util/serialize'; import { parseify } from 'common/util/serialize';
import { import { canvasBatchIdAdded, stagingAreaInitialized } from 'features/canvas/store/canvasSlice';
canvasBatchIdAdded,
stagingAreaInitialized,
} from 'features/canvas/store/canvasSlice';
import { blobToDataURL } from 'features/canvas/util/blobToDataURL'; import { blobToDataURL } from 'features/canvas/util/blobToDataURL';
import { getCanvasData } from 'features/canvas/util/getCanvasData'; import { getCanvasData } from 'features/canvas/util/getCanvasData';
import { getCanvasGenerationMode } from 'features/canvas/util/getCanvasGenerationMode'; import { getCanvasGenerationMode } from 'features/canvas/util/getCanvasGenerationMode';
@ -34,20 +31,14 @@ import { startAppListening } from '..';
export const addEnqueueRequestedCanvasListener = () => { export const addEnqueueRequestedCanvasListener = () => {
startAppListening({ startAppListening({
predicate: (action): action is ReturnType<typeof enqueueRequested> => predicate: (action): action is ReturnType<typeof enqueueRequested> =>
enqueueRequested.match(action) && enqueueRequested.match(action) && action.payload.tabName === 'unifiedCanvas',
action.payload.tabName === 'unifiedCanvas',
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
const log = logger('queue'); const log = logger('queue');
const { prepend } = action.payload; const { prepend } = action.payload;
const state = getState(); const state = getState();
const { const { layerState, boundingBoxCoordinates, boundingBoxDimensions, isMaskEnabled, shouldPreserveMaskedArea } =
layerState, state.canvas;
boundingBoxCoordinates,
boundingBoxDimensions,
isMaskEnabled,
shouldPreserveMaskedArea,
} = state.canvas;
// Build canvas blobs // Build canvas blobs
const canvasBlobsAndImageData = await getCanvasData( const canvasBlobsAndImageData = await getCanvasData(
@ -63,14 +54,10 @@ export const addEnqueueRequestedCanvasListener = () => {
return; return;
} }
const { baseBlob, baseImageData, maskBlob, maskImageData } = const { baseBlob, baseImageData, maskBlob, maskImageData } = canvasBlobsAndImageData;
canvasBlobsAndImageData;
// Determine the generation mode // Determine the generation mode
const generationMode = getCanvasGenerationMode( const generationMode = getCanvasGenerationMode(baseImageData, maskImageData);
baseImageData,
maskImageData
);
if (state.system.enableImageDebugging) { if (state.system.enableImageDebugging) {
const baseDataURL = await blobToDataURL(baseBlob); const baseDataURL = await blobToDataURL(baseBlob);
@ -115,12 +102,7 @@ export const addEnqueueRequestedCanvasListener = () => {
).unwrap(); ).unwrap();
} }
const graph = buildCanvasGraph( const graph = buildCanvasGraph(state, generationMode, canvasInitImage, canvasMaskImage);
state,
generationMode,
canvasInitImage,
canvasMaskImage
);
log.debug({ graph: parseify(graph) }, `Canvas graph built`); log.debug({ graph: parseify(graph) }, `Canvas graph built`);

View File

@ -11,9 +11,7 @@ import { startAppListening } from '..';
export const addEnqueueRequestedLinear = () => { export const addEnqueueRequestedLinear = () => {
startAppListening({ startAppListening({
predicate: (action): action is ReturnType<typeof enqueueRequested> => predicate: (action): action is ReturnType<typeof enqueueRequested> =>
enqueueRequested.match(action) && enqueueRequested.match(action) && (action.payload.tabName === 'txt2img' || action.payload.tabName === 'img2img'),
(action.payload.tabName === 'txt2img' ||
action.payload.tabName === 'img2img'),
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
const state = getState(); const state = getState();
const model = state.generation.model; const model = state.generation.model;

View File

@ -32,8 +32,7 @@ export const addGalleryImageClickedListener = () => {
const { imageDTO, shiftKey, ctrlKey, metaKey } = action.payload; const { imageDTO, shiftKey, ctrlKey, metaKey } = action.payload;
const state = getState(); const state = getState();
const queryArgs = selectListImagesQueryArgs(state); const queryArgs = selectListImagesQueryArgs(state);
const { data: listImagesData } = const { data: listImagesData } = imagesApi.endpoints.listImages.select(queryArgs)(state);
imagesApi.endpoints.listImages.select(queryArgs)(state);
if (!listImagesData) { if (!listImagesData) {
// Should never happen if we have clicked a gallery image // Should never happen if we have clicked a gallery image
@ -46,12 +45,8 @@ export const addGalleryImageClickedListener = () => {
if (shiftKey) { if (shiftKey) {
const rangeEndImageName = imageDTO.image_name; const rangeEndImageName = imageDTO.image_name;
const lastSelectedImage = selection[selection.length - 1]?.image_name; const lastSelectedImage = selection[selection.length - 1]?.image_name;
const lastClickedIndex = imageDTOs.findIndex( const lastClickedIndex = imageDTOs.findIndex((n) => n.image_name === lastSelectedImage);
(n) => n.image_name === lastSelectedImage const currentClickedIndex = imageDTOs.findIndex((n) => n.image_name === rangeEndImageName);
);
const currentClickedIndex = imageDTOs.findIndex(
(n) => n.image_name === rangeEndImageName
);
if (lastClickedIndex > -1 && currentClickedIndex > -1) { if (lastClickedIndex > -1 && currentClickedIndex > -1) {
// We have a valid range! // We have a valid range!
const start = Math.min(lastClickedIndex, currentClickedIndex); const start = Math.min(lastClickedIndex, currentClickedIndex);
@ -60,15 +55,8 @@ export const addGalleryImageClickedListener = () => {
dispatch(selectionChanged(selection.concat(imagesToSelect))); dispatch(selectionChanged(selection.concat(imagesToSelect)));
} }
} else if (ctrlKey || metaKey) { } else if (ctrlKey || metaKey) {
if ( if (selection.some((i) => i.image_name === imageDTO.image_name) && selection.length > 1) {
selection.some((i) => i.image_name === imageDTO.image_name) && dispatch(selectionChanged(selection.filter((n) => n.image_name !== imageDTO.image_name)));
selection.length > 1
) {
dispatch(
selectionChanged(
selection.filter((n) => n.image_name !== imageDTO.image_name)
)
);
} else { } else {
dispatch(selectionChanged(selection.concat(imageDTO))); dispatch(selectionChanged(selection.concat(imageDTO)));
} }

View File

@ -43,31 +43,21 @@ export const addRequestedSingleImageDeletionListener = () => {
dispatch(isModalOpenChanged(false)); dispatch(isModalOpenChanged(false));
const state = getState(); const state = getState();
const lastSelectedImage = const lastSelectedImage = state.gallery.selection[state.gallery.selection.length - 1]?.image_name;
state.gallery.selection[state.gallery.selection.length - 1]?.image_name;
if (imageDTO && imageDTO?.image_name === lastSelectedImage) { if (imageDTO && imageDTO?.image_name === lastSelectedImage) {
const { image_name } = imageDTO; const { image_name } = imageDTO;
const baseQueryArgs = selectListImagesQueryArgs(state); const baseQueryArgs = selectListImagesQueryArgs(state);
const { data } = const { data } = imagesApi.endpoints.listImages.select(baseQueryArgs)(state);
imagesApi.endpoints.listImages.select(baseQueryArgs)(state);
const cachedImageDTOs = data ? imagesSelectors.selectAll(data) : []; const cachedImageDTOs = data ? imagesSelectors.selectAll(data) : [];
const deletedImageIndex = cachedImageDTOs.findIndex( const deletedImageIndex = cachedImageDTOs.findIndex((i) => i.image_name === image_name);
(i) => i.image_name === image_name
);
const filteredImageDTOs = cachedImageDTOs.filter( const filteredImageDTOs = cachedImageDTOs.filter((i) => i.image_name !== image_name);
(i) => i.image_name !== image_name
);
const newSelectedImageIndex = clamp( const newSelectedImageIndex = clamp(deletedImageIndex, 0, filteredImageDTOs.length - 1);
deletedImageIndex,
0,
filteredImageDTOs.length - 1
);
const newSelectedImageDTO = filteredImageDTOs[newSelectedImageIndex]; const newSelectedImageDTO = filteredImageDTOs[newSelectedImageIndex];
@ -85,9 +75,7 @@ export const addRequestedSingleImageDeletionListener = () => {
imageDTOs.forEach((imageDTO) => { imageDTOs.forEach((imageDTO) => {
// reset init image if we deleted it // reset init image if we deleted it
if ( if (getState().generation.initialImage?.imageName === imageDTO.image_name) {
getState().generation.initialImage?.imageName === imageDTO.image_name
) {
dispatch(clearInitialImage()); dispatch(clearInitialImage());
} }
@ -95,8 +83,7 @@ export const addRequestedSingleImageDeletionListener = () => {
forEach(selectControlAdapterAll(getState().controlAdapters), (ca) => { forEach(selectControlAdapterAll(getState().controlAdapters), (ca) => {
if ( if (
ca.controlImage === imageDTO.image_name || ca.controlImage === imageDTO.image_name ||
(isControlNetOrT2IAdapter(ca) && (isControlNetOrT2IAdapter(ca) && ca.processedControlImage === imageDTO.image_name)
ca.processedControlImage === imageDTO.image_name)
) { ) {
dispatch( dispatch(
controlAdapterImageChanged({ controlAdapterImageChanged({
@ -120,10 +107,7 @@ export const addRequestedSingleImageDeletionListener = () => {
} }
forEach(node.data.inputs, (input) => { forEach(node.data.inputs, (input) => {
if ( if (isImageFieldInputInstance(input) && input.value?.image_name === imageDTO.image_name) {
isImageFieldInputInstance(input) &&
input.value?.image_name === imageDTO.image_name
) {
dispatch( dispatch(
fieldImageValueChanged({ fieldImageValueChanged({
nodeId: node.data.id, nodeId: node.data.id,
@ -137,24 +121,16 @@ export const addRequestedSingleImageDeletionListener = () => {
}); });
// Delete from server // Delete from server
const { requestId } = dispatch( const { requestId } = dispatch(imagesApi.endpoints.deleteImage.initiate(imageDTO));
imagesApi.endpoints.deleteImage.initiate(imageDTO)
);
// Wait for successful deletion, then trigger boards to re-fetch // Wait for successful deletion, then trigger boards to re-fetch
const wasImageDeleted = await condition( const wasImageDeleted = await condition(
(action) => (action) => imagesApi.endpoints.deleteImage.matchFulfilled(action) && action.meta.requestId === requestId,
imagesApi.endpoints.deleteImage.matchFulfilled(action) &&
action.meta.requestId === requestId,
30000 30000
); );
if (wasImageDeleted) { if (wasImageDeleted) {
dispatch( dispatch(api.util.invalidateTags([{ type: 'Board', id: imageDTO.board_id ?? 'none' }]));
api.util.invalidateTags([
{ type: 'Board', id: imageDTO.board_id ?? 'none' },
])
);
} }
}, },
}); });
@ -176,17 +152,12 @@ export const addRequestedMultipleImageDeletionListener = () => {
try { try {
// Delete from server // Delete from server
await dispatch( await dispatch(imagesApi.endpoints.deleteImages.initiate({ imageDTOs })).unwrap();
imagesApi.endpoints.deleteImages.initiate({ imageDTOs })
).unwrap();
const state = getState(); const state = getState();
const queryArgs = selectListImagesQueryArgs(state); const queryArgs = selectListImagesQueryArgs(state);
const { data } = const { data } = imagesApi.endpoints.listImages.select(queryArgs)(state);
imagesApi.endpoints.listImages.select(queryArgs)(state);
const newSelectedImageDTO = data const newSelectedImageDTO = data ? imagesSelectors.selectAll(data)[0] : undefined;
? imagesSelectors.selectAll(data)[0]
: undefined;
if (newSelectedImageDTO) { if (newSelectedImageDTO) {
dispatch(imageSelected(newSelectedImageDTO)); dispatch(imageSelected(newSelectedImageDTO));
@ -204,10 +175,7 @@ export const addRequestedMultipleImageDeletionListener = () => {
imageDTOs.forEach((imageDTO) => { imageDTOs.forEach((imageDTO) => {
// reset init image if we deleted it // reset init image if we deleted it
if ( if (getState().generation.initialImage?.imageName === imageDTO.image_name) {
getState().generation.initialImage?.imageName ===
imageDTO.image_name
) {
dispatch(clearInitialImage()); dispatch(clearInitialImage());
} }
@ -215,8 +183,7 @@ export const addRequestedMultipleImageDeletionListener = () => {
forEach(selectControlAdapterAll(getState().controlAdapters), (ca) => { forEach(selectControlAdapterAll(getState().controlAdapters), (ca) => {
if ( if (
ca.controlImage === imageDTO.image_name || ca.controlImage === imageDTO.image_name ||
(isControlNetOrT2IAdapter(ca) && (isControlNetOrT2IAdapter(ca) && ca.processedControlImage === imageDTO.image_name)
ca.processedControlImage === imageDTO.image_name)
) { ) {
dispatch( dispatch(
controlAdapterImageChanged({ controlAdapterImageChanged({
@ -240,10 +207,7 @@ export const addRequestedMultipleImageDeletionListener = () => {
} }
forEach(node.data.inputs, (input) => { forEach(node.data.inputs, (input) => {
if ( if (isImageFieldInputInstance(input) && input.value?.image_name === imageDTO.image_name) {
isImageFieldInputInstance(input) &&
input.value?.image_name === imageDTO.image_name
) {
dispatch( dispatch(
fieldImageValueChanged({ fieldImageValueChanged({
nodeId: node.data.id, nodeId: node.data.id,
@ -295,10 +259,7 @@ export const addImageDeletedRejectedListener = () => {
matcher: imagesApi.endpoints.deleteImage.matchRejected, matcher: imagesApi.endpoints.deleteImage.matchRejected,
effect: (action) => { effect: (action) => {
const log = logger('images'); const log = logger('images');
log.debug( log.debug({ imageDTO: action.meta.arg.originalArgs }, 'Unable to delete image');
{ imageDTO: action.meta.arg.originalArgs },
'Unable to delete image'
);
}, },
}); });
}; };

View File

@ -6,16 +6,10 @@ import {
controlAdapterImageChanged, controlAdapterImageChanged,
controlAdapterIsEnabledChanged, controlAdapterIsEnabledChanged,
} from 'features/controlAdapters/store/controlAdaptersSlice'; } from 'features/controlAdapters/store/controlAdaptersSlice';
import type { import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
TypesafeDraggableData,
TypesafeDroppableData,
} from 'features/dnd/types';
import { imageSelected } from 'features/gallery/store/gallerySlice'; import { imageSelected } from 'features/gallery/store/gallerySlice';
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice'; import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import { import { initialImageChanged, selectOptimalDimension } from 'features/parameters/store/generationSlice';
initialImageChanged,
selectOptimalDimension,
} from 'features/parameters/store/generationSlice';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '../'; import { startAppListening } from '../';
@ -35,15 +29,9 @@ export const addImageDroppedListener = () => {
if (activeData.payloadType === 'IMAGE_DTO') { if (activeData.payloadType === 'IMAGE_DTO') {
log.debug({ activeData, overData }, 'Image dropped'); log.debug({ activeData, overData }, 'Image dropped');
} else if (activeData.payloadType === 'GALLERY_SELECTION') { } else if (activeData.payloadType === 'GALLERY_SELECTION') {
log.debug( log.debug({ activeData, overData }, `Images (${getState().gallery.selection.length}) dropped`);
{ activeData, overData },
`Images (${getState().gallery.selection.length}) dropped`
);
} else if (activeData.payloadType === 'NODE_FIELD') { } else if (activeData.payloadType === 'NODE_FIELD') {
log.debug( log.debug({ activeData: parseify(activeData), overData: parseify(overData) }, 'Node field dropped');
{ activeData: parseify(activeData), overData: parseify(overData) },
'Node field dropped'
);
} else { } else {
log.debug({ activeData, overData }, `Unknown payload dropped`); log.debug({ activeData, overData }, `Unknown payload dropped`);
} }
@ -104,12 +92,7 @@ export const addImageDroppedListener = () => {
activeData.payloadType === 'IMAGE_DTO' && activeData.payloadType === 'IMAGE_DTO' &&
activeData.payload.imageDTO activeData.payload.imageDTO
) { ) {
dispatch( dispatch(setInitialCanvasImage(activeData.payload.imageDTO, selectOptimalDimension(getState())));
setInitialCanvasImage(
activeData.payload.imageDTO,
selectOptimalDimension(getState())
)
);
return; return;
} }
@ -191,10 +174,7 @@ export const addImageDroppedListener = () => {
/** /**
* Multiple images dropped on user board * Multiple images dropped on user board
*/ */
if ( if (overData.actionType === 'ADD_TO_BOARD' && activeData.payloadType === 'GALLERY_SELECTION') {
overData.actionType === 'ADD_TO_BOARD' &&
activeData.payloadType === 'GALLERY_SELECTION'
) {
const imageDTOs = getState().gallery.selection; const imageDTOs = getState().gallery.selection;
const { boardId } = overData.context; const { boardId } = overData.context;
dispatch( dispatch(
@ -209,10 +189,7 @@ export const addImageDroppedListener = () => {
/** /**
* Multiple images dropped on 'none' board * Multiple images dropped on 'none' board
*/ */
if ( if (overData.actionType === 'REMOVE_FROM_BOARD' && activeData.payloadType === 'GALLERY_SELECTION') {
overData.actionType === 'REMOVE_FROM_BOARD' &&
activeData.payloadType === 'GALLERY_SELECTION'
) {
const imageDTOs = getState().gallery.selection; const imageDTOs = getState().gallery.selection;
dispatch( dispatch(
imagesApi.endpoints.removeImagesFromBoard.initiate({ imagesApi.endpoints.removeImagesFromBoard.initiate({

View File

@ -1,9 +1,6 @@
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions'; import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
import { selectImageUsage } from 'features/deleteImageModal/store/selectors'; import { selectImageUsage } from 'features/deleteImageModal/store/selectors';
import { import { imagesToDeleteSelected, isModalOpenChanged } from 'features/deleteImageModal/store/slice';
imagesToDeleteSelected,
isModalOpenChanged,
} from 'features/deleteImageModal/store/slice';
import { startAppListening } from '..'; import { startAppListening } from '..';

View File

@ -1,4 +1,4 @@
import type { UseToastOptions } from '@invoke-ai/ui'; import type { UseToastOptions } from '@invoke-ai/ui-library';
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import { import {
@ -6,10 +6,7 @@ import {
controlAdapterIsEnabledChanged, controlAdapterIsEnabledChanged,
} from 'features/controlAdapters/store/controlAdaptersSlice'; } from 'features/controlAdapters/store/controlAdaptersSlice';
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice'; import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import { import { initialImageChanged, selectOptimalDimension } from 'features/parameters/store/generationSlice';
initialImageChanged,
selectOptimalDimension,
} from 'features/parameters/store/generationSlice';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next'; import { t } from 'i18next';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
@ -79,9 +76,7 @@ export const addImageUploadedFulfilledListener = () => {
} }
if (postUploadAction?.type === 'SET_CANVAS_INITIAL_IMAGE') { if (postUploadAction?.type === 'SET_CANVAS_INITIAL_IMAGE') {
dispatch( dispatch(setInitialCanvasImage(imageDTO, selectOptimalDimension(state)));
setInitialCanvasImage(imageDTO, selectOptimalDimension(state))
);
dispatch( dispatch(
addToast({ addToast({
...DEFAULT_UPLOADED_TOAST, ...DEFAULT_UPLOADED_TOAST,
@ -127,9 +122,7 @@ export const addImageUploadedFulfilledListener = () => {
if (postUploadAction?.type === 'SET_NODES_IMAGE') { if (postUploadAction?.type === 'SET_NODES_IMAGE') {
const { nodeId, fieldName } = postUploadAction; const { nodeId, fieldName } = postUploadAction;
dispatch( dispatch(fieldImageValueChanged({ nodeId, fieldName, value: imageDTO }));
fieldImageValueChanged({ nodeId, fieldName, value: imageDTO })
);
dispatch( dispatch(
addToast({ addToast({
...DEFAULT_UPLOADED_TOAST, ...DEFAULT_UPLOADED_TOAST,

View File

@ -11,11 +11,7 @@ export const addInitialImageSelectedListener = () => {
actionCreator: initialImageSelected, actionCreator: initialImageSelected,
effect: (action, { dispatch }) => { effect: (action, { dispatch }) => {
if (!action.payload) { if (!action.payload) {
dispatch( dispatch(addToast(makeToast({ title: t('toast.imageNotLoadedDesc'), status: 'error' })));
addToast(
makeToast({ title: t('toast.imageNotLoadedDesc'), status: 'error' })
)
);
return; return;
} }

View File

@ -5,10 +5,7 @@ import {
} from 'features/controlAdapters/store/controlAdaptersSlice'; } from 'features/controlAdapters/store/controlAdaptersSlice';
import { loraRemoved } from 'features/lora/store/loraSlice'; import { loraRemoved } from 'features/lora/store/loraSlice';
import { modelSelected } from 'features/parameters/store/actions'; import { modelSelected } from 'features/parameters/store/actions';
import { import { modelChanged, vaeSelected } from 'features/parameters/store/generationSlice';
modelChanged,
vaeSelected,
} from 'features/parameters/store/generationSlice';
import { zParameterModel } from 'features/parameters/types/parameterSchemas'; import { zParameterModel } from 'features/parameters/types/parameterSchemas';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast'; import { makeToast } from 'features/system/util/makeToast';
@ -27,18 +24,14 @@ export const addModelSelectedListener = () => {
const result = zParameterModel.safeParse(action.payload); const result = zParameterModel.safeParse(action.payload);
if (!result.success) { if (!result.success) {
log.error( log.error({ error: result.error.format() }, 'Failed to parse main model');
{ error: result.error.format() },
'Failed to parse main model'
);
return; return;
} }
const newModel = result.data; const newModel = result.data;
const newBaseModel = newModel.base_model; const newBaseModel = newModel.base_model;
const didBaseModelChange = const didBaseModelChange = state.generation.model?.base_model !== newBaseModel;
state.generation.model?.base_model !== newBaseModel;
if (didBaseModelChange) { if (didBaseModelChange) {
// we may need to reset some incompatible submodels // we may need to reset some incompatible submodels
@ -62,9 +55,7 @@ export const addModelSelectedListener = () => {
// handle incompatible controlnets // handle incompatible controlnets
selectControlAdapterAll(state.controlAdapters).forEach((ca) => { selectControlAdapterAll(state.controlAdapters).forEach((ca) => {
if (ca.model?.base_model !== newBaseModel) { if (ca.model?.base_model !== newBaseModel) {
dispatch( dispatch(controlAdapterIsEnabledChanged({ id: ca.id, isEnabled: false }));
controlAdapterIsEnabledChanged({ id: ca.id, isEnabled: false })
);
modelsCleared += 1; modelsCleared += 1;
} }
}); });

View File

@ -6,41 +6,24 @@ import {
selectAllT2IAdapters, selectAllT2IAdapters,
} from 'features/controlAdapters/store/controlAdaptersSlice'; } from 'features/controlAdapters/store/controlAdaptersSlice';
import { loraRemoved } from 'features/lora/store/loraSlice'; import { loraRemoved } from 'features/lora/store/loraSlice';
import { import { modelChanged, vaeSelected } from 'features/parameters/store/generationSlice';
modelChanged, import { zParameterModel, zParameterVAEModel } from 'features/parameters/types/parameterSchemas';
vaeSelected,
} from 'features/parameters/store/generationSlice';
import {
zParameterModel,
zParameterVAEModel,
} from 'features/parameters/types/parameterSchemas';
import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice'; import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice';
import { forEach, some } from 'lodash-es'; import { forEach, some } from 'lodash-es';
import { import { mainModelsAdapterSelectors, modelsApi, vaeModelsAdapterSelectors } from 'services/api/endpoints/models';
mainModelsAdapterSelectors,
modelsApi,
vaeModelsAdapterSelectors,
} from 'services/api/endpoints/models';
import type { TypeGuardFor } from 'services/api/types'; import type { TypeGuardFor } from 'services/api/types';
import { startAppListening } from '..'; import { startAppListening } from '..';
export const addModelsLoadedListener = () => { export const addModelsLoadedListener = () => {
startAppListening({ startAppListening({
predicate: ( predicate: (action): action is TypeGuardFor<typeof modelsApi.endpoints.getMainModels.matchFulfilled> =>
action
): action is TypeGuardFor<
typeof modelsApi.endpoints.getMainModels.matchFulfilled
> =>
modelsApi.endpoints.getMainModels.matchFulfilled(action) && modelsApi.endpoints.getMainModels.matchFulfilled(action) &&
!action.meta.arg.originalArgs.includes('sdxl-refiner'), !action.meta.arg.originalArgs.includes('sdxl-refiner'),
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// models loaded, we need to ensure the selected model is available and if not, select the first one // models loaded, we need to ensure the selected model is available and if not, select the first one
const log = logger('models'); const log = logger('models');
log.info( log.info({ models: action.payload.entities }, `Main models loaded (${action.payload.ids.length})`);
{ models: action.payload.entities },
`Main models loaded (${action.payload.ids.length})`
);
const currentModel = getState().generation.model; const currentModel = getState().generation.model;
const models = mainModelsAdapterSelectors.selectAll(action.payload); const models = mainModelsAdapterSelectors.selectAll(action.payload);
@ -67,10 +50,7 @@ export const addModelsLoadedListener = () => {
const result = zParameterModel.safeParse(models[0]); const result = zParameterModel.safeParse(models[0]);
if (!result.success) { if (!result.success) {
log.error( log.error({ error: result.error.format() }, 'Failed to parse main model');
{ error: result.error.format() },
'Failed to parse main model'
);
return; return;
} }
@ -78,20 +58,12 @@ export const addModelsLoadedListener = () => {
}, },
}); });
startAppListening({ startAppListening({
predicate: ( predicate: (action): action is TypeGuardFor<typeof modelsApi.endpoints.getMainModels.matchFulfilled> =>
action modelsApi.endpoints.getMainModels.matchFulfilled(action) && action.meta.arg.originalArgs.includes('sdxl-refiner'),
): action is TypeGuardFor<
typeof modelsApi.endpoints.getMainModels.matchFulfilled
> =>
modelsApi.endpoints.getMainModels.matchFulfilled(action) &&
action.meta.arg.originalArgs.includes('sdxl-refiner'),
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// models loaded, we need to ensure the selected model is available and if not, select the first one // models loaded, we need to ensure the selected model is available and if not, select the first one
const log = logger('models'); const log = logger('models');
log.info( log.info({ models: action.payload.entities }, `SDXL Refiner models loaded (${action.payload.ids.length})`);
{ models: action.payload.entities },
`SDXL Refiner models loaded (${action.payload.ids.length})`
);
const currentModel = getState().sdxl.refinerModel; const currentModel = getState().sdxl.refinerModel;
const models = mainModelsAdapterSelectors.selectAll(action.payload); const models = mainModelsAdapterSelectors.selectAll(action.payload);
@ -122,10 +94,7 @@ export const addModelsLoadedListener = () => {
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// VAEs loaded, need to reset the VAE is it's no longer available // VAEs loaded, need to reset the VAE is it's no longer available
const log = logger('models'); const log = logger('models');
log.info( log.info({ models: action.payload.entities }, `VAEs loaded (${action.payload.ids.length})`);
{ models: action.payload.entities },
`VAEs loaded (${action.payload.ids.length})`
);
const currentVae = getState().generation.vae; const currentVae = getState().generation.vae;
@ -136,9 +105,7 @@ export const addModelsLoadedListener = () => {
const isCurrentVAEAvailable = some( const isCurrentVAEAvailable = some(
action.payload.entities, action.payload.entities,
(m) => (m) => m?.model_name === currentVae?.model_name && m?.base_model === currentVae?.base_model
m?.model_name === currentVae?.model_name &&
m?.base_model === currentVae?.base_model
); );
if (isCurrentVAEAvailable) { if (isCurrentVAEAvailable) {
@ -156,10 +123,7 @@ export const addModelsLoadedListener = () => {
const result = zParameterVAEModel.safeParse(firstModel); const result = zParameterVAEModel.safeParse(firstModel);
if (!result.success) { if (!result.success) {
log.error( log.error({ error: result.error.format() }, 'Failed to parse VAE model');
{ error: result.error.format() },
'Failed to parse VAE model'
);
return; return;
} }
@ -171,19 +135,14 @@ export const addModelsLoadedListener = () => {
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// LoRA models loaded - need to remove missing LoRAs from state // LoRA models loaded - need to remove missing LoRAs from state
const log = logger('models'); const log = logger('models');
log.info( log.info({ models: action.payload.entities }, `LoRAs loaded (${action.payload.ids.length})`);
{ models: action.payload.entities },
`LoRAs loaded (${action.payload.ids.length})`
);
const loras = getState().lora.loras; const loras = getState().lora.loras;
forEach(loras, (lora, id) => { forEach(loras, (lora, id) => {
const isLoRAAvailable = some( const isLoRAAvailable = some(
action.payload.entities, action.payload.entities,
(m) => (m) => m?.model_name === lora?.model_name && m?.base_model === lora?.base_model
m?.model_name === lora?.model_name &&
m?.base_model === lora?.base_model
); );
if (isLoRAAvailable) { if (isLoRAAvailable) {
@ -199,17 +158,12 @@ export const addModelsLoadedListener = () => {
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// ControlNet models loaded - need to remove missing ControlNets from state // ControlNet models loaded - need to remove missing ControlNets from state
const log = logger('models'); const log = logger('models');
log.info( log.info({ models: action.payload.entities }, `ControlNet models loaded (${action.payload.ids.length})`);
{ models: action.payload.entities },
`ControlNet models loaded (${action.payload.ids.length})`
);
selectAllControlNets(getState().controlAdapters).forEach((ca) => { selectAllControlNets(getState().controlAdapters).forEach((ca) => {
const isModelAvailable = some( const isModelAvailable = some(
action.payload.entities, action.payload.entities,
(m) => (m) => m?.model_name === ca?.model?.model_name && m?.base_model === ca?.model?.base_model
m?.model_name === ca?.model?.model_name &&
m?.base_model === ca?.model?.base_model
); );
if (isModelAvailable) { if (isModelAvailable) {
@ -225,17 +179,12 @@ export const addModelsLoadedListener = () => {
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// ControlNet models loaded - need to remove missing ControlNets from state // ControlNet models loaded - need to remove missing ControlNets from state
const log = logger('models'); const log = logger('models');
log.info( log.info({ models: action.payload.entities }, `T2I Adapter models loaded (${action.payload.ids.length})`);
{ models: action.payload.entities },
`T2I Adapter models loaded (${action.payload.ids.length})`
);
selectAllT2IAdapters(getState().controlAdapters).forEach((ca) => { selectAllT2IAdapters(getState().controlAdapters).forEach((ca) => {
const isModelAvailable = some( const isModelAvailable = some(
action.payload.entities, action.payload.entities,
(m) => (m) => m?.model_name === ca?.model?.model_name && m?.base_model === ca?.model?.base_model
m?.model_name === ca?.model?.model_name &&
m?.base_model === ca?.model?.base_model
); );
if (isModelAvailable) { if (isModelAvailable) {
@ -251,17 +200,12 @@ export const addModelsLoadedListener = () => {
effect: async (action, { getState, dispatch }) => { effect: async (action, { getState, dispatch }) => {
// ControlNet models loaded - need to remove missing ControlNets from state // ControlNet models loaded - need to remove missing ControlNets from state
const log = logger('models'); const log = logger('models');
log.info( log.info({ models: action.payload.entities }, `IP Adapter models loaded (${action.payload.ids.length})`);
{ models: action.payload.entities },
`IP Adapter models loaded (${action.payload.ids.length})`
);
selectAllIPAdapters(getState().controlAdapters).forEach((ca) => { selectAllIPAdapters(getState().controlAdapters).forEach((ca) => {
const isModelAvailable = some( const isModelAvailable = some(
action.payload.entities, action.payload.entities,
(m) => (m) => m?.model_name === ca?.model?.model_name && m?.base_model === ca?.model?.base_model
m?.model_name === ca?.model?.model_name &&
m?.base_model === ca?.model?.base_model
); );
if (isModelAvailable) { if (isModelAvailable) {
@ -276,10 +220,7 @@ export const addModelsLoadedListener = () => {
matcher: modelsApi.endpoints.getTextualInversionModels.matchFulfilled, matcher: modelsApi.endpoints.getTextualInversionModels.matchFulfilled,
effect: async (action) => { effect: async (action) => {
const log = logger('models'); const log = logger('models');
log.info( log.info({ models: action.payload.entities }, `Embeddings loaded (${action.payload.ids.length})`);
{ models: action.payload.entities },
`Embeddings loaded (${action.payload.ids.length})`
);
}, },
}); });
}; };

View File

@ -15,21 +15,12 @@ import { socketConnected } from 'services/events/actions';
import { startAppListening } from '..'; import { startAppListening } from '..';
const matcher = isAnyOf( const matcher = isAnyOf(setPositivePrompt, combinatorialToggled, maxPromptsChanged, maxPromptsReset, socketConnected);
setPositivePrompt,
combinatorialToggled,
maxPromptsChanged,
maxPromptsReset,
socketConnected
);
export const addDynamicPromptsListener = () => { export const addDynamicPromptsListener = () => {
startAppListening({ startAppListening({
matcher, matcher,
effect: async ( effect: async (action, { dispatch, getState, cancelActiveListeners, delay }) => {
action,
{ dispatch, getState, cancelActiveListeners, delay }
) => {
cancelActiveListeners(); cancelActiveListeners();
const state = getState(); const state = getState();
const { positivePrompt } = state.generation; const { positivePrompt } = state.generation;

View File

@ -17,16 +17,9 @@ export const addReceivedOpenAPISchemaListener = () => {
log.debug({ schemaJSON }, 'Received OpenAPI schema'); log.debug({ schemaJSON }, 'Received OpenAPI schema');
const { nodesAllowlist, nodesDenylist } = getState().config; const { nodesAllowlist, nodesDenylist } = getState().config;
const nodeTemplates = parseSchema( const nodeTemplates = parseSchema(schemaJSON, nodesAllowlist, nodesDenylist);
schemaJSON,
nodesAllowlist,
nodesDenylist
);
log.debug( log.debug({ nodeTemplates: parseify(nodeTemplates) }, `Built ${size(nodeTemplates)} node templates`);
{ nodeTemplates: parseify(nodeTemplates) },
`Built ${size(nodeTemplates)} node templates`
);
dispatch(nodeTemplatesBuilt(nodeTemplates)); dispatch(nodeTemplatesBuilt(nodeTemplates));
}, },
@ -36,10 +29,7 @@ export const addReceivedOpenAPISchemaListener = () => {
actionCreator: receivedOpenAPISchema.rejected, actionCreator: receivedOpenAPISchema.rejected,
effect: (action) => { effect: (action) => {
const log = logger('system'); const log = logger('system');
log.error( log.error({ error: parseify(action.error) }, 'Problem retrieving OpenAPI Schema');
{ error: parseify(action.error) },
'Problem retrieving OpenAPI Schema'
);
}, },
}); });
}; };

View File

@ -16,10 +16,7 @@ const $isFirstConnection = atom(true);
export const addSocketConnectedEventListener = () => { export const addSocketConnectedEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketConnected, actionCreator: socketConnected,
effect: async ( effect: async (action, { dispatch, getState, cancelActiveListeners, delay }) => {
action,
{ dispatch, getState, cancelActiveListeners, delay }
) => {
log.debug('Connected'); log.debug('Connected');
/** /**
@ -86,10 +83,7 @@ export const addSocketConnectedEventListener = () => {
effect: async (action, { dispatch, getState }) => { effect: async (action, { dispatch, getState }) => {
const { nodeTemplates, config } = getState(); const { nodeTemplates, config } = getState();
// We only want to re-fetch the schema if we don't have any node templates // We only want to re-fetch the schema if we don't have any node templates
if ( if (!size(nodeTemplates.templates) && !config.disabledTabs.includes('nodes')) {
!size(nodeTemplates.templates) &&
!config.disabledTabs.includes('nodes')
) {
// This request is a createAsyncThunk - resetting API state as in the above listener // This request is a createAsyncThunk - resetting API state as in the above listener
// will not trigger this request, so we need to manually do it. // will not trigger this request, so we need to manually do it.
dispatch(receivedOpenAPISchema()); dispatch(receivedOpenAPISchema());

View File

@ -1,17 +1,10 @@
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize'; import { parseify } from 'common/util/serialize';
import { addImageToStagingArea } from 'features/canvas/store/canvasSlice'; import { addImageToStagingArea } from 'features/canvas/store/canvasSlice';
import { import { boardIdSelected, galleryViewChanged, imageSelected } from 'features/gallery/store/gallerySlice';
boardIdSelected,
galleryViewChanged,
imageSelected,
} from 'features/gallery/store/gallerySlice';
import { IMAGE_CATEGORIES } from 'features/gallery/store/types'; import { IMAGE_CATEGORIES } from 'features/gallery/store/types';
import { isImageOutput } from 'features/nodes/types/common'; import { isImageOutput } from 'features/nodes/types/common';
import { import { LINEAR_UI_OUTPUT, nodeIDDenyList } from 'features/nodes/util/graph/constants';
LINEAR_UI_OUTPUT,
nodeIDDenyList,
} from 'features/nodes/util/graph/constants';
import { boardsApi } from 'services/api/endpoints/boards'; import { boardsApi } from 'services/api/endpoints/boards';
import { imagesApi } from 'services/api/endpoints/images'; import { imagesApi } from 'services/api/endpoints/images';
import { imagesAdapter } from 'services/api/util'; import { imagesAdapter } from 'services/api/util';
@ -29,19 +22,12 @@ export const addInvocationCompleteEventListener = () => {
actionCreator: socketInvocationComplete, actionCreator: socketInvocationComplete,
effect: async (action, { dispatch, getState }) => { effect: async (action, { dispatch, getState }) => {
const { data } = action.payload; const { data } = action.payload;
log.debug( log.debug({ data: parseify(data) }, `Invocation complete (${action.payload.data.node.type})`);
{ data: parseify(data) },
`Invocation complete (${action.payload.data.node.type})`
);
const { result, node, queue_batch_id, source_node_id } = data; const { result, node, queue_batch_id, source_node_id } = data;
// This complete event has an associated image output // This complete event has an associated image output
if ( if (isImageOutput(result) && !nodeTypeDenylist.includes(node.type) && !nodeIDDenyList.includes(source_node_id)) {
isImageOutput(result) &&
!nodeTypeDenylist.includes(node.type) &&
!nodeIDDenyList.includes(source_node_id)
) {
const { image_name } = result.image; const { image_name } = result.image;
const { canvas, gallery } = getState(); const { canvas, gallery } = getState();
@ -56,10 +42,7 @@ export const addInvocationCompleteEventListener = () => {
imageDTORequest.unsubscribe(); imageDTORequest.unsubscribe();
// Add canvas images to the staging area // Add canvas images to the staging area
if ( if (canvas.batchIds.includes(queue_batch_id) && [LINEAR_UI_OUTPUT].includes(data.source_node_id)) {
canvas.batchIds.includes(queue_batch_id) &&
[LINEAR_UI_OUTPUT].includes(data.source_node_id)
) {
dispatch(addImageToStagingArea(imageDTO)); dispatch(addImageToStagingArea(imageDTO));
} }
@ -84,21 +67,13 @@ export const addInvocationCompleteEventListener = () => {
// update the total images for the board // update the total images for the board
dispatch( dispatch(
boardsApi.util.updateQueryData( boardsApi.util.updateQueryData('getBoardImagesTotal', imageDTO.board_id ?? 'none', (draft) => {
'getBoardImagesTotal', // eslint-disable-next-line @typescript-eslint/no-unused-vars
imageDTO.board_id ?? 'none', draft.total += 1;
(draft) => { })
// eslint-disable-next-line @typescript-eslint/no-unused-vars
draft.total += 1;
}
)
); );
dispatch( dispatch(imagesApi.util.invalidateTags([{ type: 'Board', id: imageDTO.board_id ?? 'none' }]));
imagesApi.util.invalidateTags([
{ type: 'Board', id: imageDTO.board_id ?? 'none' },
])
);
const { shouldAutoSwitch } = gallery; const { shouldAutoSwitch } = gallery;
@ -109,10 +84,7 @@ export const addInvocationCompleteEventListener = () => {
dispatch(galleryViewChanged('images')); dispatch(galleryViewChanged('images'));
} }
if ( if (imageDTO.board_id && imageDTO.board_id !== gallery.selectedBoardId) {
imageDTO.board_id &&
imageDTO.board_id !== gallery.selectedBoardId
) {
dispatch( dispatch(
boardIdSelected({ boardIdSelected({
boardId: imageDTO.board_id, boardId: imageDTO.board_id,

View File

@ -9,10 +9,7 @@ export const addInvocationErrorEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketInvocationError, actionCreator: socketInvocationError,
effect: (action) => { effect: (action) => {
log.error( log.error(action.payload, `Invocation error (${action.payload.data.node.type})`);
action.payload,
`Invocation error (${action.payload.data.node.type})`
);
}, },
}); });
}; };

View File

@ -9,10 +9,7 @@ export const addInvocationRetrievalErrorEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketInvocationRetrievalError, actionCreator: socketInvocationRetrievalError,
effect: (action) => { effect: (action) => {
log.error( log.error(action.payload, `Invocation retrieval error (${action.payload.data.graph_execution_state_id})`);
action.payload,
`Invocation retrieval error (${action.payload.data.graph_execution_state_id})`
);
}, },
}); });
}; };

View File

@ -9,10 +9,7 @@ export const addInvocationStartedEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketInvocationStarted, actionCreator: socketInvocationStarted,
effect: (action) => { effect: (action) => {
log.debug( log.debug(action.payload, `Invocation started (${action.payload.data.node.type})`);
action.payload,
`Invocation started (${action.payload.data.node.type})`
);
}, },
}); });
}; };

View File

@ -1,8 +1,5 @@
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import { import { socketModelLoadCompleted, socketModelLoadStarted } from 'services/events/actions';
socketModelLoadCompleted,
socketModelLoadStarted,
} from 'services/events/actions';
import { startAppListening } from '../..'; import { startAppListening } from '../..';
@ -12,8 +9,7 @@ export const addModelLoadEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketModelLoadStarted, actionCreator: socketModelLoadStarted,
effect: (action) => { effect: (action) => {
const { base_model, model_name, model_type, submodel } = const { base_model, model_name, model_type, submodel } = action.payload.data;
action.payload.data;
let message = `Model load started: ${base_model}/${model_type}/${model_name}`; let message = `Model load started: ${base_model}/${model_type}/${model_name}`;
@ -28,8 +24,7 @@ export const addModelLoadEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketModelLoadCompleted, actionCreator: socketModelLoadCompleted,
effect: (action) => { effect: (action) => {
const { base_model, model_name, model_type, submodel } = const { base_model, model_name, model_type, submodel } = action.payload.data;
action.payload.data;
let message = `Model load complete: ${base_model}/${model_type}/${model_name}`; let message = `Model load complete: ${base_model}/${model_type}/${model_name}`;

View File

@ -13,10 +13,7 @@ export const addSocketQueueItemStatusChangedEventListener = () => {
// we've got new status for the queue item, batch and queue // we've got new status for the queue item, batch and queue
const { queue_item, batch_status, queue_status } = action.payload.data; const { queue_item, batch_status, queue_status } = action.payload.data;
log.debug( log.debug(action.payload, `Queue item ${queue_item.item_id} status updated: ${queue_item.status}`);
action.payload,
`Queue item ${queue_item.item_id} status updated: ${queue_item.status}`
);
// Update this specific queue item in the list of queue items (this is the queue item DTO, without the session) // Update this specific queue item in the list of queue items (this is the queue item DTO, without the session)
dispatch( dispatch(
@ -40,35 +37,23 @@ export const addSocketQueueItemStatusChangedEventListener = () => {
// Update the batch status // Update the batch status
dispatch( dispatch(
queueApi.util.updateQueryData( queueApi.util.updateQueryData('getBatchStatus', { batch_id: batch_status.batch_id }, () => batch_status)
'getBatchStatus',
{ batch_id: batch_status.batch_id },
() => batch_status
)
); );
// Update the queue item status (this is the full queue item, including the session) // Update the queue item status (this is the full queue item, including the session)
dispatch( dispatch(
queueApi.util.updateQueryData( queueApi.util.updateQueryData('getQueueItem', queue_item.item_id, (draft) => {
'getQueueItem', if (!draft) {
queue_item.item_id, return;
(draft) => {
if (!draft) {
return;
}
Object.assign(draft, queue_item);
} }
) Object.assign(draft, queue_item);
})
); );
// Invalidate caches for things we cannot update // Invalidate caches for things we cannot update
// TODO: technically, we could possibly update the current session queue item, but feels safer to just request it again // TODO: technically, we could possibly update the current session queue item, but feels safer to just request it again
dispatch( dispatch(
queueApi.util.invalidateTags([ queueApi.util.invalidateTags(['CurrentSessionQueueItem', 'NextSessionQueueItem', 'InvocationCacheStatus'])
'CurrentSessionQueueItem',
'NextSessionQueueItem',
'InvocationCacheStatus',
])
); );
}, },
}); });

View File

@ -9,10 +9,7 @@ export const addSessionRetrievalErrorEventListener = () => {
startAppListening({ startAppListening({
actionCreator: socketSessionRetrievalError, actionCreator: socketSessionRetrievalError,
effect: (action) => { effect: (action) => {
log.error( log.error(action.payload, `Session retrieval error (${action.payload.data.graph_execution_state_id})`);
action.payload,
`Session retrieval error (${action.payload.data.graph_execution_state_id})`
);
}, },
}); });
}; };

View File

@ -3,10 +3,7 @@ import { updateAllNodesRequested } from 'features/nodes/store/actions';
import { nodeReplaced } from 'features/nodes/store/nodesSlice'; import { nodeReplaced } from 'features/nodes/store/nodesSlice';
import { NodeUpdateError } from 'features/nodes/types/error'; import { NodeUpdateError } from 'features/nodes/types/error';
import { isInvocationNode } from 'features/nodes/types/invocation'; import { isInvocationNode } from 'features/nodes/types/invocation';
import { import { getNeedsUpdate, updateNode } from 'features/nodes/util/node/nodeUpdate';
getNeedsUpdate,
updateNode,
} from 'features/nodes/util/node/nodeUpdate';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast'; import { makeToast } from 'features/system/util/makeToast';
import { t } from 'i18next'; import { t } from 'i18next';

View File

@ -10,9 +10,7 @@ import type { BatchConfig, ImageDTO } from 'services/api/types';
import { startAppListening } from '..'; import { startAppListening } from '..';
export const upscaleRequested = createAction<{ imageDTO: ImageDTO }>( export const upscaleRequested = createAction<{ imageDTO: ImageDTO }>(`upscale/upscaleRequested`);
`upscale/upscaleRequested`
);
export const addUpscaleRequestedListener = () => { export const addUpscaleRequestedListener = () => {
startAppListening({ startAppListening({
@ -24,8 +22,7 @@ export const addUpscaleRequestedListener = () => {
const { image_name } = imageDTO; const { image_name } = imageDTO;
const state = getState(); const state = getState();
const { isAllowedToUpscale, detailTKey } = const { isAllowedToUpscale, detailTKey } = createIsAllowedToUpscaleSelector(imageDTO)(state);
createIsAllowedToUpscaleSelector(imageDTO)(state);
// if we can't upscale, show a toast and return // if we can't upscale, show a toast and return
if (!isAllowedToUpscale) { if (!isAllowedToUpscale) {
@ -66,21 +63,11 @@ export const addUpscaleRequestedListener = () => {
const enqueueResult = await req.unwrap(); const enqueueResult = await req.unwrap();
req.reset(); req.reset();
log.debug( log.debug({ enqueueResult: parseify(enqueueResult) }, t('queue.graphQueued'));
{ enqueueResult: parseify(enqueueResult) },
t('queue.graphQueued')
);
} catch (error) { } catch (error) {
log.error( log.error({ enqueueBatchArg: parseify(enqueueBatchArg) }, t('queue.graphFailedToQueue'));
{ enqueueBatchArg: parseify(enqueueBatchArg) },
t('queue.graphFailedToQueue')
);
if ( if (error instanceof Object && 'status' in error && error.status === 403) {
error instanceof Object &&
'status' in error &&
error.status === 403
) {
return; return;
} else { } else {
dispatch( dispatch(

View File

@ -1,14 +1,8 @@
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize'; import { parseify } from 'common/util/serialize';
import { import { workflowLoaded, workflowLoadRequested } from 'features/nodes/store/actions';
workflowLoaded,
workflowLoadRequested,
} from 'features/nodes/store/actions';
import { $flow } from 'features/nodes/store/reactFlowInstance'; import { $flow } from 'features/nodes/store/reactFlowInstance';
import { import { WorkflowMigrationError, WorkflowVersionError } from 'features/nodes/types/error';
WorkflowMigrationError,
WorkflowVersionError,
} from 'features/nodes/types/error';
import { validateWorkflow } from 'features/nodes/util/workflow/validateWorkflow'; import { validateWorkflow } from 'features/nodes/util/workflow/validateWorkflow';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast'; import { makeToast } from 'features/system/util/makeToast';
@ -28,10 +22,7 @@ export const addWorkflowLoadRequestedListener = () => {
const nodeTemplates = getState().nodeTemplates.templates; const nodeTemplates = getState().nodeTemplates.templates;
try { try {
const { workflow: validatedWorkflow, warnings } = validateWorkflow( const { workflow: validatedWorkflow, warnings } = validateWorkflow(workflow, nodeTemplates);
workflow,
nodeTemplates
);
if (asCopy) { if (asCopy) {
// If we're loading a copy, we need to remove the ID so that the backend will create a new workflow // If we're loading a copy, we need to remove the ID so that the backend will create a new workflow
@ -108,10 +99,7 @@ export const addWorkflowLoadRequestedListener = () => {
); );
} else { } else {
// Some other error occurred // Some other error occurred
log.error( log.error({ error: parseify(e) }, t('nodes.unknownErrorValidatingWorkflow'));
{ error: parseify(e) },
t('nodes.unknownErrorValidatingWorkflow')
);
dispatch( dispatch(
addToast( addToast(
makeToast({ makeToast({

View File

@ -1,4 +1,4 @@
import type { MenuItemProps } from '@invoke-ai/ui'; import type { MenuItemProps } from '@invoke-ai/ui-library';
import { atom } from 'nanostores'; import { atom } from 'nanostores';
export type CustomStarUi = { export type CustomStarUi = {

View File

@ -8,6 +8,4 @@ declare global {
} }
} }
export const $store = atom< export const $store = atom<Readonly<ReturnType<typeof createStore>> | undefined>();
Readonly<ReturnType<typeof createStore>> | undefined
>();

View File

@ -1,7 +1,4 @@
import type { WorkflowCategory } from 'features/nodes/types/workflow'; import type { WorkflowCategory } from 'features/nodes/types/workflow';
import { atom } from 'nanostores'; import { atom } from 'nanostores';
export const $workflowCategories = atom<WorkflowCategory[]>([ export const $workflowCategories = atom<WorkflowCategory[]>(['user', 'default']);
'user',
'default',
]);

View File

@ -1,17 +1,10 @@
import type { ThunkDispatch, UnknownAction } from '@reduxjs/toolkit'; import type { ThunkDispatch, UnknownAction } from '@reduxjs/toolkit';
import { import { autoBatchEnhancer, combineReducers, configureStore } from '@reduxjs/toolkit';
autoBatchEnhancer,
combineReducers,
configureStore,
} from '@reduxjs/toolkit';
import { logger } from 'app/logging/logger'; import { logger } from 'app/logging/logger';
import { idbKeyValDriver } from 'app/store/enhancers/reduxRemember/driver'; import { idbKeyValDriver } from 'app/store/enhancers/reduxRemember/driver';
import { errorHandler } from 'app/store/enhancers/reduxRemember/errors'; import { errorHandler } from 'app/store/enhancers/reduxRemember/errors';
import { canvasPersistDenylist } from 'features/canvas/store/canvasPersistDenylist'; import { canvasPersistDenylist } from 'features/canvas/store/canvasPersistDenylist';
import canvasReducer, { import canvasReducer, { initialCanvasState, migrateCanvasState } from 'features/canvas/store/canvasSlice';
initialCanvasState,
migrateCanvasState,
} from 'features/canvas/store/canvasSlice';
import changeBoardModalReducer from 'features/changeBoardModal/store/slice'; import changeBoardModalReducer from 'features/changeBoardModal/store/slice';
import { controlAdaptersPersistDenylist } from 'features/controlAdapters/store/controlAdaptersPersistDenylist'; import { controlAdaptersPersistDenylist } from 'features/controlAdapters/store/controlAdaptersPersistDenylist';
import controlAdaptersReducer, { import controlAdaptersReducer, {
@ -25,32 +18,17 @@ import dynamicPromptsReducer, {
migrateDynamicPromptsState, migrateDynamicPromptsState,
} from 'features/dynamicPrompts/store/dynamicPromptsSlice'; } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { galleryPersistDenylist } from 'features/gallery/store/galleryPersistDenylist'; import { galleryPersistDenylist } from 'features/gallery/store/galleryPersistDenylist';
import galleryReducer, { import galleryReducer, { initialGalleryState, migrateGalleryState } from 'features/gallery/store/gallerySlice';
initialGalleryState, import hrfReducer, { initialHRFState, migrateHRFState } from 'features/hrf/store/hrfSlice';
migrateGalleryState, import loraReducer, { initialLoraState, migrateLoRAState } from 'features/lora/store/loraSlice';
} from 'features/gallery/store/gallerySlice';
import hrfReducer, {
initialHRFState,
migrateHRFState,
} from 'features/hrf/store/hrfSlice';
import loraReducer, {
initialLoraState,
migrateLoRAState,
} from 'features/lora/store/loraSlice';
import modelmanagerReducer, { import modelmanagerReducer, {
initialModelManagerState, initialModelManagerState,
migrateModelManagerState, migrateModelManagerState,
} from 'features/modelManager/store/modelManagerSlice'; } from 'features/modelManager/store/modelManagerSlice';
import { nodesPersistDenylist } from 'features/nodes/store/nodesPersistDenylist'; import { nodesPersistDenylist } from 'features/nodes/store/nodesPersistDenylist';
import nodesReducer, { import nodesReducer, { initialNodesState, migrateNodesState } from 'features/nodes/store/nodesSlice';
initialNodesState,
migrateNodesState,
} from 'features/nodes/store/nodesSlice';
import nodeTemplatesReducer from 'features/nodes/store/nodeTemplatesSlice'; import nodeTemplatesReducer from 'features/nodes/store/nodeTemplatesSlice';
import workflowReducer, { import workflowReducer, { initialWorkflowState, migrateWorkflowState } from 'features/nodes/store/workflowSlice';
initialWorkflowState,
migrateWorkflowState,
} from 'features/nodes/store/workflowSlice';
import { generationPersistDenylist } from 'features/parameters/store/generationPersistDenylist'; import { generationPersistDenylist } from 'features/parameters/store/generationPersistDenylist';
import generationReducer, { import generationReducer, {
initialGenerationState, initialGenerationState,
@ -62,21 +40,12 @@ import postprocessingReducer, {
migratePostprocessingState, migratePostprocessingState,
} from 'features/parameters/store/postprocessingSlice'; } from 'features/parameters/store/postprocessingSlice';
import queueReducer from 'features/queue/store/queueSlice'; import queueReducer from 'features/queue/store/queueSlice';
import sdxlReducer, { import sdxlReducer, { initialSDXLState, migrateSDXLState } from 'features/sdxl/store/sdxlSlice';
initialSDXLState,
migrateSDXLState,
} from 'features/sdxl/store/sdxlSlice';
import configReducer from 'features/system/store/configSlice'; import configReducer from 'features/system/store/configSlice';
import { systemPersistDenylist } from 'features/system/store/systemPersistDenylist'; import { systemPersistDenylist } from 'features/system/store/systemPersistDenylist';
import systemReducer, { import systemReducer, { initialSystemState, migrateSystemState } from 'features/system/store/systemSlice';
initialSystemState,
migrateSystemState,
} from 'features/system/store/systemSlice';
import { uiPersistDenylist } from 'features/ui/store/uiPersistDenylist'; import { uiPersistDenylist } from 'features/ui/store/uiPersistDenylist';
import uiReducer, { import uiReducer, { initialUIState, migrateUIState } from 'features/ui/store/uiSlice';
initialUIState,
migrateUIState,
} from 'features/ui/store/uiSlice';
import { diff } from 'jsondiffpatch'; import { diff } from 'jsondiffpatch';
import { defaultsDeep, keys, omit, pick } from 'lodash-es'; import { defaultsDeep, keys, omit, pick } from 'lodash-es';
import dynamicMiddlewares from 'redux-dynamic-middlewares'; import dynamicMiddlewares from 'redux-dynamic-middlewares';
@ -206,10 +175,7 @@ const unserialize: UnserializeFunction = (data, key) => {
); );
return transformed; return transformed;
} catch (err) { } catch (err) {
log.warn( log.warn({ error: serializeError(err) }, `Error rehydrating slice "${key}", falling back to default initial state`);
{ error: serializeError(err) },
`Error rehydrating slice "${key}", falling back to default initial state`
);
return config.initialState; return config.initialState;
} }
}; };
@ -229,10 +195,7 @@ const serializationDenylist: {
}; };
export const serialize: SerializeFunction = (data, key) => { export const serialize: SerializeFunction = (data, key) => {
const result = omit( const result = omit(data, serializationDenylist[key as keyof typeof serializationDenylist] ?? []);
data,
serializationDenylist[key as keyof typeof serializationDenylist] ?? []
);
return JSON.stringify(result); return JSON.stringify(result);
}; };
@ -256,9 +219,7 @@ export const createStore = (uniqueStoreKey?: string, persist = true) =>
persistDebounce: 300, persistDebounce: 300,
serialize, serialize,
unserialize, unserialize,
prefix: uniqueStoreKey prefix: uniqueStoreKey ? `${STORAGE_PREFIX}${uniqueStoreKey}-` : STORAGE_PREFIX,
? `${STORAGE_PREFIX}${uniqueStoreKey}-`
: STORAGE_PREFIX,
errorHandler, errorHandler,
}) })
); );
@ -278,9 +239,7 @@ export const createStore = (uniqueStoreKey?: string, persist = true) =>
}, },
}); });
export type AppGetState = ReturnType< export type AppGetState = ReturnType<ReturnType<typeof createStore>['getState']>;
ReturnType<typeof createStore>['getState']
>;
export type RootState = ReturnType<ReturnType<typeof createStore>['getState']>; export type RootState = ReturnType<ReturnType<typeof createStore>['getState']>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AppThunkDispatch = ThunkDispatch<RootState, any, UnknownAction>; export type AppThunkDispatch = ThunkDispatch<RootState, any, UnknownAction>;

View File

@ -1,17 +1,9 @@
import type { ChakraProps } from '@invoke-ai/ui'; import type { ChakraProps } from '@invoke-ai/ui-library';
import { import { CompositeNumberInput, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
CompositeNumberInput,
Flex,
FormControl,
FormLabel,
} from '@invoke-ai/ui';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import { memo, useCallback } from 'react'; import { memo, useCallback } from 'react';
import { RgbaColorPicker } from 'react-colorful'; import { RgbaColorPicker } from 'react-colorful';
import type { import type { ColorPickerBaseProps, RgbaColor } from 'react-colorful/dist/types';
ColorPickerBaseProps,
RgbaColor,
} from 'react-colorful/dist/types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
type IAIColorPickerProps = ColorPickerBaseProps<RgbaColor> & { type IAIColorPickerProps = ColorPickerBaseProps<RgbaColor> & {
@ -39,30 +31,13 @@ const numberInputWidth: ChakraProps['w'] = '4.2rem';
const IAIColorPicker = (props: IAIColorPickerProps) => { const IAIColorPicker = (props: IAIColorPickerProps) => {
const { color, onChange, withNumberInput, ...rest } = props; const { color, onChange, withNumberInput, ...rest } = props;
const { t } = useTranslation(); const { t } = useTranslation();
const handleChangeR = useCallback( const handleChangeR = useCallback((r: number) => onChange({ ...color, r }), [color, onChange]);
(r: number) => onChange({ ...color, r }), const handleChangeG = useCallback((g: number) => onChange({ ...color, g }), [color, onChange]);
[color, onChange] const handleChangeB = useCallback((b: number) => onChange({ ...color, b }), [color, onChange]);
); const handleChangeA = useCallback((a: number) => onChange({ ...color, a }), [color, onChange]);
const handleChangeG = useCallback(
(g: number) => onChange({ ...color, g }),
[color, onChange]
);
const handleChangeB = useCallback(
(b: number) => onChange({ ...color, b }),
[color, onChange]
);
const handleChangeA = useCallback(
(a: number) => onChange({ ...color, a }),
[color, onChange]
);
return ( return (
<Flex sx={sx}> <Flex sx={sx}>
<RgbaColorPicker <RgbaColorPicker color={color} onChange={onChange} style={colorPickerStyles} {...rest} />
color={color}
onChange={onChange}
style={colorPickerStyles}
{...rest}
/>
{withNumberInput && ( {withNumberInput && (
<Flex> <Flex>
<FormControl> <FormControl>

View File

@ -1,22 +1,11 @@
import type { ChakraProps, FlexProps, SystemStyleObject } from '@invoke-ai/ui'; import type { ChakraProps, FlexProps, SystemStyleObject } from '@invoke-ai/ui-library';
import { Flex, Icon, Image } from '@invoke-ai/ui'; import { Flex, Icon, Image } from '@invoke-ai/ui-library';
import { import { IAILoadingImageFallback, IAINoContentFallback } from 'common/components/IAIImageFallback';
IAILoadingImageFallback,
IAINoContentFallback,
} from 'common/components/IAIImageFallback';
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay'; import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
import { useImageUploadButton } from 'common/hooks/useImageUploadButton'; import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
import type { import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
TypesafeDraggableData,
TypesafeDroppableData,
} from 'features/dnd/types';
import ImageContextMenu from 'features/gallery/components/ImageContextMenu/ImageContextMenu'; import ImageContextMenu from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
import type { import type { MouseEvent, ReactElement, ReactNode, SyntheticEvent } from 'react';
MouseEvent,
ReactElement,
ReactNode,
SyntheticEvent,
} from 'react';
import { memo, useCallback, useMemo, useState } from 'react'; import { memo, useCallback, useMemo, useState } from 'react';
import { PiImageBold, PiUploadSimpleBold } from 'react-icons/pi'; import { PiImageBold, PiUploadSimpleBold } from 'react-icons/pi';
import type { ImageDTO, PostUploadAction } from 'services/api/types'; import type { ImageDTO, PostUploadAction } from 'services/api/types';
@ -161,14 +150,8 @@ const IAIDndImage = (props: IAIDndImageProps) => {
<Image <Image
src={thumbnail ? imageDTO.thumbnail_url : imageDTO.image_url} src={thumbnail ? imageDTO.thumbnail_url : imageDTO.image_url}
fallbackStrategy="beforeLoadOrError" fallbackStrategy="beforeLoadOrError"
fallbackSrc={ fallbackSrc={useThumbailFallback ? imageDTO.thumbnail_url : undefined}
useThumbailFallback ? imageDTO.thumbnail_url : undefined fallback={useThumbailFallback ? undefined : <IAILoadingImageFallback image={imageDTO} />}
}
fallback={
useThumbailFallback ? undefined : (
<IAILoadingImageFallback image={imageDTO} />
)
}
onError={onError} onError={onError}
draggable={false} draggable={false}
w={imageDTO.width} w={imageDTO.width}
@ -179,13 +162,8 @@ const IAIDndImage = (props: IAIDndImageProps) => {
sx={imageSx} sx={imageSx}
data-testid={dataTestId} data-testid={dataTestId}
/> />
{withMetadataOverlay && ( {withMetadataOverlay && <ImageMetadataOverlay imageDTO={imageDTO} />}
<ImageMetadataOverlay imageDTO={imageDTO} /> <SelectionOverlay isSelected={isSelected} isHovered={withHoverOverlay ? isHovered : false} />
)}
<SelectionOverlay
isSelected={isSelected}
isHovered={withHoverOverlay ? isHovered : false}
/>
</Flex> </Flex>
)} )}
{!imageDTO && !isUploadDisabled && ( {!imageDTO && !isUploadDisabled && (
@ -198,20 +176,10 @@ const IAIDndImage = (props: IAIDndImageProps) => {
)} )}
{!imageDTO && isUploadDisabled && noContentFallback} {!imageDTO && isUploadDisabled && noContentFallback}
{imageDTO && !isDragDisabled && ( {imageDTO && !isDragDisabled && (
<IAIDraggable <IAIDraggable data={draggableData} disabled={isDragDisabled || !imageDTO} onClick={onClick} />
data={draggableData}
disabled={isDragDisabled || !imageDTO}
onClick={onClick}
/>
)} )}
{children} {children}
{!isDropDisabled && ( {!isDropDisabled && <IAIDroppable data={droppableData} disabled={isDropDisabled} dropLabel={dropLabel} />}
<IAIDroppable
data={droppableData}
disabled={isDropDisabled}
dropLabel={dropLabel}
/>
)}
</Flex> </Flex>
)} )}
</ImageContextMenu> </ImageContextMenu>

View File

@ -1,5 +1,5 @@
import type { SystemStyleObject } from '@invoke-ai/ui'; import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { IconButton } from '@invoke-ai/ui'; import { IconButton } from '@invoke-ai/ui-library';
import type { MouseEvent, ReactElement } from 'react'; import type { MouseEvent, ReactElement } from 'react';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';

View File

@ -1,5 +1,5 @@
import type { BoxProps } from '@invoke-ai/ui'; import type { BoxProps } from '@invoke-ai/ui-library';
import { Box } from '@invoke-ai/ui'; import { Box } from '@invoke-ai/ui-library';
import { useDraggableTypesafe } from 'features/dnd/hooks/typesafeHooks'; import { useDraggableTypesafe } from 'features/dnd/hooks/typesafeHooks';
import type { TypesafeDraggableData } from 'features/dnd/types'; import type { TypesafeDraggableData } from 'features/dnd/types';
import { memo, useRef } from 'react'; import { memo, useRef } from 'react';

View File

@ -1,4 +1,4 @@
import { Box, Flex } from '@invoke-ai/ui'; import { Box, Flex } from '@invoke-ai/ui-library';
import type { AnimationProps } from 'framer-motion'; import type { AnimationProps } from 'framer-motion';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
@ -27,12 +27,7 @@ const IAIDropOverlay = (props: Props) => {
const { isOver, label = t('gallery.drop') } = props; const { isOver, label = t('gallery.drop') } = props;
const motionId = useRef(uuidv4()); const motionId = useRef(uuidv4());
return ( return (
<motion.div <motion.div key={motionId.current} initial={initial} animate={animate} exit={exit}>
key={motionId.current}
initial={initial}
animate={animate}
exit={exit}
>
<Flex position="absolute" top={0} insetInlineStart={0} w="full" h="full"> <Flex position="absolute" top={0} insetInlineStart={0} w="full" h="full">
<Flex <Flex
position="absolute" position="absolute"

View File

@ -1,4 +1,4 @@
import { Box } from '@invoke-ai/ui'; import { Box } from '@invoke-ai/ui-library';
import { useDroppableTypesafe } from 'features/dnd/hooks/typesafeHooks'; import { useDroppableTypesafe } from 'features/dnd/hooks/typesafeHooks';
import type { TypesafeDroppableData } from 'features/dnd/types'; import type { TypesafeDroppableData } from 'features/dnd/types';
import { isValidDrop } from 'features/dnd/util/isValidDrop'; import { isValidDrop } from 'features/dnd/util/isValidDrop';
@ -36,9 +36,7 @@ const IAIDroppable = (props: IAIDroppableProps) => {
pointerEvents={active ? 'auto' : 'none'} pointerEvents={active ? 'auto' : 'none'}
> >
<AnimatePresence> <AnimatePresence>
{isValidDrop(data, active) && ( {isValidDrop(data, active) && <IAIDropOverlay isOver={isOver} label={dropLabel} />}
<IAIDropOverlay isOver={isOver} label={dropLabel} />
)}
</AnimatePresence> </AnimatePresence>
</Box> </Box>
); );

View File

@ -1,5 +1,5 @@
import type { SystemStyleObject } from '@invoke-ai/ui'; import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { Box, Skeleton } from '@invoke-ai/ui'; import { Box, Skeleton } from '@invoke-ai/ui-library';
import { memo } from 'react'; import { memo } from 'react';
const skeletonStyles: SystemStyleObject = { const skeletonStyles: SystemStyleObject = {
@ -16,13 +16,7 @@ const skeletonStyles: SystemStyleObject = {
const IAIFillSkeleton = () => { const IAIFillSkeleton = () => {
return ( return (
<Skeleton sx={skeletonStyles}> <Skeleton sx={skeletonStyles}>
<Box <Box position="absolute" top={0} insetInlineStart={0} height="full" width="full" />
position="absolute"
top={0}
insetInlineStart={0}
height="full"
width="full"
/>
</Skeleton> </Skeleton>
); );
}; };

View File

@ -1,5 +1,5 @@
import type { As, ChakraProps, FlexProps } from '@invoke-ai/ui'; import type { As, ChakraProps, FlexProps } from '@invoke-ai/ui-library';
import { Flex, Icon, Skeleton, Spinner, Text } from '@invoke-ai/ui'; import { Flex, Icon, Skeleton, Spinner, Text } from '@invoke-ai/ui-library';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
import { PiImageBold } from 'react-icons/pi'; import { PiImageBold } from 'react-icons/pi';
import type { ImageDTO } from 'services/api/types'; import type { ImageDTO } from 'services/api/types';
@ -19,15 +19,7 @@ export const IAILoadingImageFallback = memo((props: Props) => {
} }
return ( return (
<Flex <Flex opacity={0.7} w="full" h="full" alignItems="center" justifyContent="center" borderRadius="base" bg="base.900">
opacity={0.7}
w="full"
h="full"
alignItems="center"
justifyContent="center"
borderRadius="base"
bg="base.900"
>
<Spinner size="xl" /> <Spinner size="xl" />
</Flex> </Flex>
); );
@ -77,32 +69,30 @@ type IAINoImageFallbackWithSpinnerProps = FlexProps & {
label?: string; label?: string;
}; };
export const IAINoContentFallbackWithSpinner = memo( export const IAINoContentFallbackWithSpinner = memo((props: IAINoImageFallbackWithSpinnerProps) => {
(props: IAINoImageFallbackWithSpinnerProps) => { const { sx, ...rest } = props;
const { sx, ...rest } = props; const styles = useMemo(
const styles = useMemo( () => ({
() => ({ w: 'full',
w: 'full', h: 'full',
h: 'full', alignItems: 'center',
alignItems: 'center', justifyContent: 'center',
justifyContent: 'center', borderRadius: 'base',
borderRadius: 'base', flexDir: 'column',
flexDir: 'column', gap: 2,
gap: 2, userSelect: 'none',
userSelect: 'none', opacity: 0.7,
opacity: 0.7, color: 'base.500',
color: 'base.500', ...sx,
...sx, }),
}), [sx]
[sx] );
);
return ( return (
<Flex sx={styles} {...rest}> <Flex sx={styles} {...rest}>
<Spinner size="xl" /> <Spinner size="xl" />
{props.label && <Text textAlign="center">{props.label}</Text>} {props.label && <Text textAlign="center">{props.label}</Text>}
</Flex> </Flex>
); );
} });
);
IAINoContentFallbackWithSpinner.displayName = 'IAINoContentFallbackWithSpinner'; IAINoContentFallbackWithSpinner.displayName = 'IAINoContentFallbackWithSpinner';

View File

@ -1,4 +1,4 @@
import { Badge, Flex } from '@invoke-ai/ui'; import { Badge, Flex } from '@invoke-ai/ui-library';
import { memo } from 'react'; import { memo } from 'react';
import type { ImageDTO } from 'services/api/types'; import type { ImageDTO } from 'services/api/types';

View File

@ -1,4 +1,4 @@
import { Box, Flex, Heading } from '@invoke-ai/ui'; import { Box, Flex, Heading } from '@invoke-ai/ui-library';
import type { AnimationProps } from 'framer-motion'; import type { AnimationProps } from 'framer-motion';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { memo } from 'react'; import { memo } from 'react';
@ -91,9 +91,7 @@ const ImageUploadOverlay = (props: ImageUploadOverlayProps) => {
) : ( ) : (
<> <>
<Heading size="lg">{t('toast.invalidUpload')}</Heading> <Heading size="lg">{t('toast.invalidUpload')}</Heading>
<Heading size="md"> <Heading size="md">{t('toast.uploadFailedInvalidUploadDesc')}</Heading>
{t('toast.uploadFailedInvalidUploadDesc')}
</Heading>
</> </>
)} )}
</Flex> </Flex>

View File

@ -11,7 +11,7 @@ import {
PopoverTrigger, PopoverTrigger,
Portal, Portal,
Text, Text,
} from '@invoke-ai/ui'; } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { merge, omit } from 'lodash-es'; import { merge, omit } from 'lodash-es';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
@ -28,46 +28,39 @@ type Props = {
children: ReactElement; children: ReactElement;
}; };
export const InformationalPopover = memo( export const InformationalPopover = memo(({ feature, children, inPortal = true, ...rest }: Props) => {
({ feature, children, inPortal = true, ...rest }: Props) => { const shouldEnableInformationalPopovers = useAppSelector((s) => s.system.shouldEnableInformationalPopovers);
const shouldEnableInformationalPopovers = useAppSelector(
(s) => s.system.shouldEnableInformationalPopovers
);
const data = useMemo(() => POPOVER_DATA[feature], [feature]); const data = useMemo(() => POPOVER_DATA[feature], [feature]);
const popoverProps = useMemo( const popoverProps = useMemo(() => merge(omit(data, ['image', 'href', 'buttonLabel']), rest), [data, rest]);
() => merge(omit(data, ['image', 'href', 'buttonLabel']), rest),
[data, rest]
);
if (!shouldEnableInformationalPopovers) { if (!shouldEnableInformationalPopovers) {
return children; return children;
}
return (
<Popover
isLazy
closeOnBlur={false}
trigger="hover"
variant="informational"
openDelay={OPEN_DELAY}
modifiers={POPPER_MODIFIERS}
placement="top"
{...popoverProps}
>
<PopoverTrigger>{children}</PopoverTrigger>
{inPortal ? (
<Portal>
<Content data={data} feature={feature} />
</Portal>
) : (
<Content data={data} feature={feature} />
)}
</Popover>
);
} }
);
return (
<Popover
isLazy
closeOnBlur={false}
trigger="hover"
variant="informational"
openDelay={OPEN_DELAY}
modifiers={POPPER_MODIFIERS}
placement="top"
{...popoverProps}
>
<PopoverTrigger>{children}</PopoverTrigger>
{inPortal ? (
<Portal>
<Content data={data} feature={feature} />
</Portal>
) : (
<Content data={data} feature={feature} />
)}
</Popover>
);
});
InformationalPopover.displayName = 'InformationalPopover'; InformationalPopover.displayName = 'InformationalPopover';
@ -79,10 +72,7 @@ type ContentProps = {
const Content = ({ data, feature }: ContentProps) => { const Content = ({ data, feature }: ContentProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const heading = useMemo<string | undefined>( const heading = useMemo<string | undefined>(() => t(`popovers.${feature}.heading`), [feature, t]);
() => t(`popovers.${feature}.heading`),
[feature, t]
);
const paragraphs = useMemo<string[]>( const paragraphs = useMemo<string[]>(
() => () =>

View File

@ -1,4 +1,4 @@
import type { PopoverProps } from '@invoke-ai/ui'; import type { PopoverProps } from '@invoke-ai/ui-library';
export type Feature = export type Feature =
| 'clipSkip' | 'clipSkip'
@ -95,6 +95,4 @@ export const POPOVER_DATA: { [key in Feature]?: PopoverData } = {
export const OPEN_DELAY = 1000; // in milliseconds export const OPEN_DELAY = 1000; // in milliseconds
export const POPPER_MODIFIERS: PopoverProps['modifiers'] = [ export const POPPER_MODIFIERS: PopoverProps['modifiers'] = [{ name: 'preventOverflow', options: { padding: 10 } }];
{ name: 'preventOverflow', options: { padding: 10 } },
];

View File

@ -1,4 +1,4 @@
import { Flex, Image, Spinner } from '@invoke-ai/ui'; import { Flex, Image, Spinner } from '@invoke-ai/ui-library';
import InvokeLogoWhite from 'public/assets/images/invoke-symbol-wht-lrg.svg'; import InvokeLogoWhite from 'public/assets/images/invoke-symbol-wht-lrg.svg';
import { memo } from 'react'; import { memo } from 'react';
@ -6,14 +6,7 @@ import { memo } from 'react';
const Loading = () => { const Loading = () => {
return ( return (
<Flex <Flex position="relative" width="100vw" height="100vh" alignItems="center" justifyContent="center" bg="#151519">
position="relative"
width="100vw"
height="100vh"
alignItems="center"
justifyContent="center"
bg="#151519"
>
<Image src={InvokeLogoWhite} w="8rem" h="8rem" /> <Image src={InvokeLogoWhite} w="8rem" h="8rem" />
<Spinner <Spinner
label="Loading" label="Loading"

View File

@ -1,4 +1,4 @@
import { Box } from '@invoke-ai/ui'; import { Box } from '@invoke-ai/ui-library';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
type Props = { type Props = {

View File

@ -1,5 +1,5 @@
import type { ChakraProps } from '@invoke-ai/ui'; import type { ChakraProps } from '@invoke-ai/ui-library';
import { Box, Flex } from '@invoke-ai/ui'; import { Box, Flex } from '@invoke-ai/ui-library';
import { getOverlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants'; import { getOverlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { CSSProperties, PropsWithChildren } from 'react'; import type { CSSProperties, PropsWithChildren } from 'react';
@ -13,12 +13,7 @@ type Props = PropsWithChildren & {
const styles: CSSProperties = { height: '100%', width: '100%' }; const styles: CSSProperties = { height: '100%', width: '100%' };
const ScrollableContent = ({ const ScrollableContent = ({ children, maxHeight, overflowX = 'hidden', overflowY = 'scroll' }: Props) => {
children,
maxHeight,
overflowX = 'hidden',
overflowY = 'scroll',
}: Props) => {
const overlayscrollbarsOptions = useMemo( const overlayscrollbarsOptions = useMemo(
() => getOverlayScrollbarsParams(overflowX, overflowY).options, () => getOverlayScrollbarsParams(overflowX, overflowY).options,
[overflowX, overflowY] [overflowX, overflowY]
@ -26,11 +21,7 @@ const ScrollableContent = ({
return ( return (
<Flex w="full" h="full" maxHeight={maxHeight} position="relative"> <Flex w="full" h="full" maxHeight={maxHeight} position="relative">
<Box position="absolute" top={0} left={0} right={0} bottom={0}> <Box position="absolute" top={0} left={0} right={0} bottom={0}>
<OverlayScrollbarsComponent <OverlayScrollbarsComponent defer style={styles} options={overlayscrollbarsOptions}>
defer
style={styles}
options={overlayscrollbarsOptions}
>
{children} {children}
</OverlayScrollbarsComponent> </OverlayScrollbarsComponent>
</Box> </Box>

View File

@ -1,4 +1,4 @@
import { Box } from '@invoke-ai/ui'; import { Box } from '@invoke-ai/ui-library';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
type Props = { type Props = {

View File

@ -1,4 +1,4 @@
import { useToken } from '@invoke-ai/ui'; import { useToken } from '@invoke-ai/ui-library';
export const useChakraThemeTokens = () => { export const useChakraThemeTokens = () => {
const [ const [

View File

@ -14,22 +14,19 @@ const accept: Accept = {
'image/jpeg': ['.jpg', '.jpeg', '.png'], 'image/jpeg': ['.jpg', '.jpeg', '.png'],
}; };
const selectPostUploadAction = createMemoizedSelector( const selectPostUploadAction = createMemoizedSelector(activeTabNameSelector, (activeTabName) => {
activeTabNameSelector, let postUploadAction: PostUploadAction = { type: 'TOAST' };
(activeTabName) => {
let postUploadAction: PostUploadAction = { type: 'TOAST' };
if (activeTabName === 'unifiedCanvas') { if (activeTabName === 'unifiedCanvas') {
postUploadAction = { type: 'SET_CANVAS_INITIAL_IMAGE' }; postUploadAction = { type: 'SET_CANVAS_INITIAL_IMAGE' };
}
if (activeTabName === 'img2img') {
postUploadAction = { type: 'SET_INITIAL_IMAGE' };
}
return postUploadAction;
} }
);
if (activeTabName === 'img2img') {
postUploadAction = { type: 'SET_INITIAL_IMAGE' };
}
return postUploadAction;
});
export const useFullscreenDropzone = () => { export const useFullscreenDropzone = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -112,9 +109,7 @@ export const useFullscreenDropzone = () => {
// Set the files on the dropzone.inputRef // Set the files on the dropzone.inputRef
dropzone.inputRef.current.files = e.clipboardData.files; dropzone.inputRef.current.files = e.clipboardData.files;
// Dispatch the change event, dropzone catches this and we get to use its own validation // Dispatch the change event, dropzone catches this and we get to use its own validation
dropzone.inputRef.current?.dispatchEvent( dropzone.inputRef.current?.dispatchEvent(new Event('change', { bubbles: true }));
new Event('change', { bubbles: true })
);
} }
}; };

View File

@ -9,13 +9,8 @@ import { useHotkeys } from 'react-hotkeys-hook';
export const useGlobalHotkeys = () => { export const useGlobalHotkeys = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const isModelManagerEnabled = const isModelManagerEnabled = useFeatureStatus('modelManager').isFeatureEnabled;
useFeatureStatus('modelManager').isFeatureEnabled; const { queueBack, isDisabled: isDisabledQueueBack, isLoading: isLoadingQueueBack } = useQueueBack();
const {
queueBack,
isDisabled: isDisabledQueueBack,
isLoading: isLoadingQueueBack,
} = useQueueBack();
useHotkeys( useHotkeys(
['ctrl+enter', 'meta+enter'], ['ctrl+enter', 'meta+enter'],
@ -28,11 +23,7 @@ export const useGlobalHotkeys = () => {
[queueBack, isDisabledQueueBack, isLoadingQueueBack] [queueBack, isDisabledQueueBack, isLoadingQueueBack]
); );
const { const { queueFront, isDisabled: isDisabledQueueFront, isLoading: isLoadingQueueFront } = useQueueFront();
queueFront,
isDisabled: isDisabledQueueFront,
isLoading: isLoadingQueueFront,
} = useQueueFront();
useHotkeys( useHotkeys(
['ctrl+shift+enter', 'meta+shift+enter'], ['ctrl+shift+enter', 'meta+shift+enter'],
@ -61,11 +52,7 @@ export const useGlobalHotkeys = () => {
[cancelQueueItem, isDisabledCancelQueueItem, isLoadingCancelQueueItem] [cancelQueueItem, isDisabledCancelQueueItem, isLoadingCancelQueueItem]
); );
const { const { clearQueue, isDisabled: isDisabledClearQueue, isLoading: isLoadingClearQueue } = useClearQueue();
clearQueue,
isDisabled: isDisabledClearQueue,
isLoading: isLoadingClearQueue,
} = useClearQueue();
useHotkeys( useHotkeys(
['ctrl+shift+x', 'meta+shift+x'], ['ctrl+shift+x', 'meta+shift+x'],

View File

@ -1,4 +1,4 @@
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui'; import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
import type { EntityState } from '@reduxjs/toolkit'; import type { EntityState } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import type { GroupBase } from 'chakra-react-select'; import type { GroupBase } from 'chakra-react-select';
@ -28,11 +28,8 @@ export const useGroupedModelCombobox = <T extends AnyModelConfigEntity>(
arg: UseGroupedModelComboboxArg<T> arg: UseGroupedModelComboboxArg<T>
): UseGroupedModelComboboxReturn => { ): UseGroupedModelComboboxReturn => {
const { t } = useTranslation(); const { t } = useTranslation();
const base_model = useAppSelector( const base_model = useAppSelector((s) => s.generation.model?.base_model ?? 'sdxl');
(s) => s.generation.model?.base_model ?? 'sdxl' const { modelEntities, selectedModel, getIsDisabled, onChange, isLoading } = arg;
);
const { modelEntities, selectedModel, getIsDisabled, onChange, isLoading } =
arg;
const options = useMemo<GroupBase<ComboboxOption>[]>(() => { const options = useMemo<GroupBase<ComboboxOption>[]>(() => {
if (!modelEntities) { if (!modelEntities) {
return []; return [];
@ -60,11 +57,8 @@ export const useGroupedModelCombobox = <T extends AnyModelConfigEntity>(
const value = useMemo( const value = useMemo(
() => () =>
options options.flatMap((o) => o.options).find((m) => (selectedModel ? m.value === getModelId(selectedModel) : false)) ??
.flatMap((o) => o.options) null,
.find((m) =>
selectedModel ? m.value === getModelId(selectedModel) : false
) ?? null,
[options, selectedModel] [options, selectedModel]
); );

View File

@ -28,10 +28,7 @@ type UseImageUploadButtonArgs = {
* <Button {...getUploadButtonProps()} /> // will open the file dialog on click * <Button {...getUploadButtonProps()} /> // will open the file dialog on click
* <input {...getUploadInputProps()} /> // hidden, handles native upload functionality * <input {...getUploadInputProps()} /> // hidden, handles native upload functionality
*/ */
export const useImageUploadButton = ({ export const useImageUploadButton = ({ postUploadAction, isDisabled }: UseImageUploadButtonArgs) => {
postUploadAction,
isDisabled,
}: UseImageUploadButtonArgs) => {
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId); const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
const [uploadImage] = useUploadImageMutation(); const [uploadImage] = useUploadImageMutation();
const onDropAccepted = useCallback( const onDropAccepted = useCallback(

View File

@ -27,15 +27,7 @@ const selector = createMemoizedSelector(
selectDynamicPromptsSlice, selectDynamicPromptsSlice,
activeTabNameSelector, activeTabNameSelector,
], ],
( (controlAdapters, generation, system, nodes, nodeTemplates, dynamicPrompts, activeTabName) => {
controlAdapters,
generation,
system,
nodes,
nodeTemplates,
dynamicPrompts,
activeTabName
) => {
const { initialImage, model, positivePrompt } = generation; const { initialImage, model, positivePrompt } = generation;
const { isConnected } = system; const { isConnected } = system;
@ -75,8 +67,7 @@ const selector = createMemoizedSelector(
forEach(node.data.inputs, (field) => { forEach(node.data.inputs, (field) => {
const fieldTemplate = nodeTemplate.inputs[field.name]; const fieldTemplate = nodeTemplate.inputs[field.name];
const hasConnection = connectedEdges.some( const hasConnection = connectedEdges.some(
(edge) => (edge) => edge.target === node.id && edge.targetHandle === field.name
edge.target === node.id && edge.targetHandle === field.name
); );
if (!fieldTemplate) { if (!fieldTemplate) {
@ -84,11 +75,7 @@ const selector = createMemoizedSelector(
return; return;
} }
if ( if (fieldTemplate.required && field.value === undefined && !hasConnection) {
fieldTemplate.required &&
field.value === undefined &&
!hasConnection
) {
reasons.push( reasons.push(
i18n.t('parameters.invoke.missingInputForField', { i18n.t('parameters.invoke.missingInputForField', {
nodeLabel: node.data.label || nodeTemplate.title, nodeLabel: node.data.label || nodeTemplate.title,
@ -101,10 +88,7 @@ const selector = createMemoizedSelector(
}); });
} }
} else { } else {
if ( if (dynamicPrompts.prompts.length === 0 && getShouldProcessPrompt(positivePrompt)) {
dynamicPrompts.prompts.length === 0 &&
getShouldProcessPrompt(positivePrompt)
) {
reasons.push(i18n.t('parameters.invoke.noPrompts')); reasons.push(i18n.t('parameters.invoke.noPrompts'));
} }
@ -134,9 +118,7 @@ const selector = createMemoizedSelector(
if ( if (
!ca.controlImage || !ca.controlImage ||
(isControlNetOrT2IAdapter(ca) && (isControlNetOrT2IAdapter(ca) && !ca.processedControlImage && ca.processorType !== 'none')
!ca.processedControlImage &&
ca.processorType !== 'none')
) { ) {
reasons.push( reasons.push(
i18n.t('parameters.invoke.noControlImageForControlAdapter', { i18n.t('parameters.invoke.noControlImageForControlAdapter', {

View File

@ -1,4 +1,4 @@
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui'; import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
import type { EntityState } from '@reduxjs/toolkit'; import type { EntityState } from '@reduxjs/toolkit';
import { map } from 'lodash-es'; import { map } from 'lodash-es';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
@ -27,14 +27,7 @@ export const useModelCombobox = <T extends AnyModelConfigEntity>(
arg: UseModelComboboxArg<T> arg: UseModelComboboxArg<T>
): UseModelComboboxReturn => { ): UseModelComboboxReturn => {
const { t } = useTranslation(); const { t } = useTranslation();
const { const { modelEntities, selectedModel, getIsDisabled, onChange, isLoading, optionsFilter = () => true } = arg;
modelEntities,
selectedModel,
getIsDisabled,
onChange,
isLoading,
optionsFilter = () => true,
} = arg;
const options = useMemo<ComboboxOption[]>(() => { const options = useMemo<ComboboxOption[]>(() => {
if (!modelEntities) { if (!modelEntities) {
return []; return [];
@ -49,10 +42,7 @@ export const useModelCombobox = <T extends AnyModelConfigEntity>(
}, [optionsFilter, getIsDisabled, modelEntities]); }, [optionsFilter, getIsDisabled, modelEntities]);
const value = useMemo( const value = useMemo(
() => () => options.find((m) => (selectedModel ? m.value === getModelId(selectedModel) : false)),
options.find((m) =>
selectedModel ? m.value === getModelId(selectedModel) : false
),
[options, selectedModel] [options, selectedModel]
); );

View File

@ -20,12 +20,7 @@ export const areAnyPixelsBlack = (pixels: Uint8ClampedArray) => {
const len = pixels.length; const len = pixels.length;
let i = 0; let i = 0;
for (i; i < len; ) { for (i; i < len; ) {
if ( if (pixels[i++] === 0 && pixels[i++] === 0 && pixels[i++] === 0 && pixels[i++] === 255) {
pixels[i++] === 0 &&
pixels[i++] === 0 &&
pixels[i++] === 0 &&
pixels[i++] === 255
) {
return true; return true;
} }
} }

View File

@ -1,2 +1 @@
export const colorTokenToCssVar = (colorToken: string) => export const colorTokenToCssVar = (colorToken: string) => `var(--invoke-colors-${colorToken.split('.').join('-')})`;
`var(--invoke-colors-${colorToken.split('.').join('-')})`;

View File

@ -8,12 +8,7 @@ export type GenerateSeedsArg = {
max?: number; max?: number;
}; };
export const generateSeeds = ({ export const generateSeeds = ({ count, start, min = NUMPY_RAND_MIN, max = NUMPY_RAND_MAX }: GenerateSeedsArg) => {
count,
start,
min = NUMPY_RAND_MIN,
max = NUMPY_RAND_MAX,
}: GenerateSeedsArg) => {
const first = start ?? random(min, max); const first = start ?? random(min, max);
const seeds: number[] = []; const seeds: number[] = [];
for (let i = first; i < first + count; i++) { for (let i = first; i < first + count; i++) {
@ -22,7 +17,4 @@ export const generateSeeds = ({
return seeds; return seeds;
}; };
export const generateOneSeed = ( export const generateOneSeed = (min: number = NUMPY_RAND_MIN, max: number = NUMPY_RAND_MAX) => random(min, max);
min: number = NUMPY_RAND_MIN,
max: number = NUMPY_RAND_MAX
) => random(min, max);

View File

@ -1,10 +1,7 @@
export const roundDownToMultiple = (num: number, multiple: number): number => { export const roundDownToMultiple = (num: number, multiple: number): number => {
return Math.floor(num / multiple) * multiple; return Math.floor(num / multiple) * multiple;
}; };
export const roundDownToMultipleMin = ( export const roundDownToMultipleMin = (num: number, multiple: number): number => {
num: number,
multiple: number
): number => {
return Math.max(multiple, Math.floor(num / multiple) * multiple); return Math.max(multiple, Math.floor(num / multiple) * multiple);
}; };

View File

@ -1,4 +1,4 @@
import { Button, ConfirmationAlertDialog, useDisclosure } from '@invoke-ai/ui'; import { Button, ConfirmationAlertDialog, useDisclosure } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { clearCanvasHistory } from 'features/canvas/store/canvasSlice'; import { clearCanvasHistory } from 'features/canvas/store/canvasSlice';
@ -11,19 +11,11 @@ const ClearCanvasHistoryButtonModal = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const acceptCallback = useCallback( const acceptCallback = useCallback(() => dispatch(clearCanvasHistory()), [dispatch]);
() => dispatch(clearCanvasHistory()),
[dispatch]
);
return ( return (
<> <>
<Button <Button onClick={onOpen} size="sm" leftIcon={<PiTrashSimpleFill />} isDisabled={isStaging}>
onClick={onOpen}
size="sm"
leftIcon={<PiTrashSimpleFill />}
isDisabled={isStaging}
>
{t('unifiedCanvas.clearCanvasHistory')} {t('unifiedCanvas.clearCanvasHistory')}
</Button> </Button>
<ConfirmationAlertDialog <ConfirmationAlertDialog

View File

@ -1,4 +1,4 @@
import { Box, chakra, Flex } from '@invoke-ai/ui'; import { Box, chakra, Flex } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
@ -19,10 +19,7 @@ import {
$tool, $tool,
} from 'features/canvas/store/canvasNanostore'; } from 'features/canvas/store/canvasNanostore';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { import { canvasResized, selectCanvasSlice } from 'features/canvas/store/canvasSlice';
canvasResized,
selectCanvasSlice,
} from 'features/canvas/store/canvasSlice';
import type Konva from 'konva'; import type Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node'; import type { KonvaEventObject } from 'konva/lib/Node';
import type { Vector2d } from 'konva/lib/types'; import type { Vector2d } from 'konva/lib/types';
@ -55,18 +52,12 @@ const ChakraStage = chakra(Stage, {
const IAICanvas = () => { const IAICanvas = () => {
const isStaging = useAppSelector(isStagingSelector); const isStaging = useAppSelector(isStagingSelector);
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled); const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
const shouldShowBoundingBox = useAppSelector( const shouldShowBoundingBox = useAppSelector((s) => s.canvas.shouldShowBoundingBox);
(s) => s.canvas.shouldShowBoundingBox
);
const shouldShowGrid = useAppSelector((s) => s.canvas.shouldShowGrid); const shouldShowGrid = useAppSelector((s) => s.canvas.shouldShowGrid);
const stageScale = useAppSelector((s) => s.canvas.stageScale); const stageScale = useAppSelector((s) => s.canvas.stageScale);
const shouldShowIntermediates = useAppSelector( const shouldShowIntermediates = useAppSelector((s) => s.canvas.shouldShowIntermediates);
(s) => s.canvas.shouldShowIntermediates
);
const shouldAntialias = useAppSelector((s) => s.canvas.shouldAntialias); const shouldAntialias = useAppSelector((s) => s.canvas.shouldAntialias);
const shouldRestrictStrokesToBox = useAppSelector( const shouldRestrictStrokesToBox = useAppSelector((s) => s.canvas.shouldRestrictStrokesToBox);
(s) => s.canvas.shouldRestrictStrokesToBox
);
const { stageCoordinates, stageDimensions } = useAppSelector(selector); const { stageCoordinates, stageDimensions } = useAppSelector(selector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
@ -95,22 +86,12 @@ const IAICanvas = () => {
return 'default'; return 'default';
} }
return 'none'; return 'none';
}, [ }, [isMouseOverBoundingBox, isMovingStage, isStaging, isTransformingBoundingBox, shouldRestrictStrokesToBox, tool]);
isMouseOverBoundingBox,
isMovingStage,
isStaging,
isTransformingBoundingBox,
shouldRestrictStrokesToBox,
tool,
]);
const canvasBaseLayerRefCallback = useCallback( const canvasBaseLayerRefCallback = useCallback((layerElement: Konva.Layer) => {
(layerElement: Konva.Layer) => { $canvasBaseLayer.set(layerElement);
$canvasBaseLayer.set(layerElement); canvasBaseLayerRef.current = layerElement;
canvasBaseLayerRef.current = layerElement; }, []);
},
[]
);
const lastCursorPositionRef = useRef<Vector2d>({ x: 0, y: 0 }); const lastCursorPositionRef = useRef<Vector2d>({ x: 0, y: 0 });
@ -120,18 +101,10 @@ const IAICanvas = () => {
const handleWheel = useCanvasWheel(stageRef); const handleWheel = useCanvasWheel(stageRef);
const handleMouseDown = useCanvasMouseDown(stageRef); const handleMouseDown = useCanvasMouseDown(stageRef);
const handleMouseUp = useCanvasMouseUp(stageRef, didMouseMoveRef); const handleMouseUp = useCanvasMouseUp(stageRef, didMouseMoveRef);
const handleMouseMove = useCanvasMouseMove( const handleMouseMove = useCanvasMouseMove(stageRef, didMouseMoveRef, lastCursorPositionRef);
stageRef, const { handleDragStart, handleDragMove, handleDragEnd } = useCanvasDragMove();
didMouseMoveRef,
lastCursorPositionRef
);
const { handleDragStart, handleDragMove, handleDragEnd } =
useCanvasDragMove();
const handleMouseOut = useCanvasMouseOut(); const handleMouseOut = useCanvasMouseOut();
const handleContextMenu = useCallback( const handleContextMenu = useCallback((e: KonvaEventObject<MouseEvent>) => e.evt.preventDefault(), []);
(e: KonvaEventObject<MouseEvent>) => e.evt.preventDefault(),
[]
);
useEffect(() => { useEffect(() => {
if (!containerRef.current) { if (!containerRef.current) {
@ -169,14 +142,7 @@ const IAICanvas = () => {
const scale = useMemo(() => ({ x: stageScale, y: stageScale }), [stageScale]); const scale = useMemo(() => ({ x: stageScale, y: stageScale }), [stageScale]);
return ( return (
<Flex <Flex id="canvas-container" ref={containerRef} position="relative" height="100%" width="100%" borderRadius="base">
id="canvas-container"
ref={containerRef}
position="relative"
height="100%"
width="100%"
borderRadius="base"
>
<Box position="absolute"> <Box position="absolute">
<ChakraStage <ChakraStage
tabIndex={-1} tabIndex={-1}
@ -205,19 +171,10 @@ const IAICanvas = () => {
<IAICanvasGrid /> <IAICanvasGrid />
</Layer> </Layer>
<Layer <Layer id="base" ref={canvasBaseLayerRefCallback} listening={false} imageSmoothingEnabled={shouldAntialias}>
id="base"
ref={canvasBaseLayerRefCallback}
listening={false}
imageSmoothingEnabled={shouldAntialias}
>
<IAICanvasObjectRenderer /> <IAICanvasObjectRenderer />
</Layer> </Layer>
<Layer <Layer id="mask" visible={isMaskEnabled && !isStaging} listening={false}>
id="mask"
visible={isMaskEnabled && !isStaging}
listening={false}
>
<IAICanvasMaskLines visible={true} listening={false} /> <IAICanvasMaskLines visible={true} listening={false} />
<IAICanvasMaskCompositer listening={false} /> <IAICanvasMaskCompositer listening={false} />
</Layer> </Layer>
@ -225,17 +182,10 @@ const IAICanvas = () => {
<IAICanvasBoundingBoxOverlay /> <IAICanvasBoundingBoxOverlay />
</Layer> </Layer>
<Layer id="preview" imageSmoothingEnabled={shouldAntialias}> <Layer id="preview" imageSmoothingEnabled={shouldAntialias}>
{!isStaging && ( {!isStaging && <IAICanvasToolPreview visible={tool !== 'move'} listening={false} />}
<IAICanvasToolPreview
visible={tool !== 'move'}
listening={false}
/>
)}
<IAICanvasStagingArea listening={false} visible={isStaging} /> <IAICanvasStagingArea listening={false} visible={isStaging} />
{shouldShowIntermediates && <IAICanvasIntermediateImage />} {shouldShowIntermediates && <IAICanvasIntermediateImage />}
<IAICanvasBoundingBox <IAICanvasBoundingBox visible={shouldShowBoundingBox && !isStaging} />
visible={shouldShowBoundingBox && !isStaging}
/>
</Layer> </Layer>
</ChakraStage> </ChakraStage>
</Box> </Box>

View File

@ -5,12 +5,7 @@ import { memo } from 'react';
import { Group, Rect } from 'react-konva'; import { Group, Rect } from 'react-konva';
const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => { const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
const { const { boundingBoxCoordinates, boundingBoxDimensions, stageDimensions, stageCoordinates } = canvas;
boundingBoxCoordinates,
boundingBoxDimensions,
stageDimensions,
stageCoordinates,
} = canvas;
return { return {
boundingBoxCoordinates, boundingBoxCoordinates,
@ -21,15 +16,8 @@ const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
}); });
const IAICanvasBoundingBoxOverlay = () => { const IAICanvasBoundingBoxOverlay = () => {
const { const { boundingBoxCoordinates, boundingBoxDimensions, stageCoordinates, stageDimensions } = useAppSelector(selector);
boundingBoxCoordinates, const shouldDarkenOutsideBoundingBox = useAppSelector((s) => s.canvas.shouldDarkenOutsideBoundingBox);
boundingBoxDimensions,
stageCoordinates,
stageDimensions,
} = useAppSelector(selector);
const shouldDarkenOutsideBoundingBox = useAppSelector(
(s) => s.canvas.shouldDarkenOutsideBoundingBox
);
const stageScale = useAppSelector((s) => s.canvas.stageScale); const stageScale = useAppSelector((s) => s.canvas.stageScale);
return ( return (

View File

@ -1,5 +1,5 @@
// Grid drawing adapted from https://longviewcoder.com/2021/12/08/konva-a-better-grid/ // Grid drawing adapted from https://longviewcoder.com/2021/12/08/konva-a-better-grid/
import { getArbitraryBaseColor } from '@invoke-ai/ui'; import { getArbitraryBaseColor } from '@invoke-ai/ui-library';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice'; import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
@ -76,11 +76,11 @@ const IAICanvasGrid = () => {
}; };
const // find the x & y size of the grid const // find the x & y size of the grid
xSize = gridFullRect.x2 - gridFullRect.x1, xSize = gridFullRect.x2 - gridFullRect.x1;
ySize = gridFullRect.y2 - gridFullRect.y1, const ySize = gridFullRect.y2 - gridFullRect.y1;
// compute the number of steps required on each axis. // compute the number of steps required on each axis.
xSteps = Math.round(xSize / gridSpacing) + 1, const xSteps = Math.round(xSize / gridSpacing) + 1;
ySteps = Math.round(ySize / gridSpacing) + 1; const ySteps = Math.round(ySize / gridSpacing) + 1;
const strokeWidth = unscale(1); const strokeWidth = unscale(1);

View File

@ -13,13 +13,8 @@ type IAICanvasImageProps = {
}; };
const IAICanvasImage = (props: IAICanvasImageProps) => { const IAICanvasImage = (props: IAICanvasImageProps) => {
const { x, y, imageName } = props.canvasImage; const { x, y, imageName } = props.canvasImage;
const { currentData: imageDTO, isError } = useGetImageDTOQuery( const { currentData: imageDTO, isError } = useGetImageDTOQuery(imageName ?? skipToken);
imageName ?? skipToken const [image, status] = useImage(imageDTO?.image_url ?? '', $authToken.get() ? 'use-credentials' : 'anonymous');
);
const [image, status] = useImage(
imageDTO?.image_url ?? '',
$authToken.get() ? 'use-credentials' : 'anonymous'
);
if (isError || status === 'failed') { if (isError || status === 'failed') {
return <IAICanvasImageErrorFallback canvasImage={props.canvasImage} />; return <IAICanvasImageErrorFallback canvasImage={props.canvasImage} />;

View File

@ -1,4 +1,4 @@
import { useToken } from '@invoke-ai/ui'; import { useToken } from '@invoke-ai/ui-library';
import type { CanvasImage } from 'features/canvas/store/canvasTypes'; import type { CanvasImage } from 'features/canvas/store/canvasTypes';
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -7,9 +7,7 @@ import { Group, Rect, Text } from 'react-konva';
type IAICanvasImageErrorFallbackProps = { type IAICanvasImageErrorFallbackProps = {
canvasImage: CanvasImage; canvasImage: CanvasImage;
}; };
const IAICanvasImageErrorFallback = ({ const IAICanvasImageErrorFallback = ({ canvasImage }: IAICanvasImageErrorFallbackProps) => {
canvasImage,
}: IAICanvasImageErrorFallbackProps) => {
const [rectFill, textFill] = useToken('colors', ['base.500', 'base.900']); const [rectFill, textFill] = useToken('colors', ['base.500', 'base.900']);
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (

View File

@ -5,26 +5,20 @@ import { selectSystemSlice } from 'features/system/store/systemSlice';
import { memo, useEffect, useState } from 'react'; import { memo, useEffect, useState } from 'react';
import { Image as KonvaImage } from 'react-konva'; import { Image as KonvaImage } from 'react-konva';
const progressImageSelector = createMemoizedSelector( const progressImageSelector = createMemoizedSelector([selectSystemSlice, selectCanvasSlice], (system, canvas) => {
[selectSystemSlice, selectCanvasSlice], const { denoiseProgress } = system;
(system, canvas) => { const { batchIds } = canvas;
const { denoiseProgress } = system;
const { batchIds } = canvas;
return { return {
progressImage: progressImage:
denoiseProgress && batchIds.includes(denoiseProgress.batch_id) denoiseProgress && batchIds.includes(denoiseProgress.batch_id) ? denoiseProgress.progress_image : undefined,
? denoiseProgress.progress_image boundingBox: canvas.layerState.stagingArea.boundingBox,
: undefined, };
boundingBox: canvas.layerState.stagingArea.boundingBox, });
};
}
);
const IAICanvasIntermediateImage = () => { const IAICanvasIntermediateImage = () => {
const { progressImage, boundingBox } = useAppSelector(progressImageSelector); const { progressImage, boundingBox } = useAppSelector(progressImageSelector);
const [loadedImageElement, setLoadedImageElement] = const [loadedImageElement, setLoadedImageElement] = useState<HTMLImageElement | null>(null);
useState<HTMLImageElement | null>(null);
useEffect(() => { useEffect(() => {
if (!progressImage) { if (!progressImage) {

View File

@ -9,30 +9,22 @@ import { isNumber } from 'lodash-es';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Rect } from 'react-konva'; import { Rect } from 'react-konva';
export const canvasMaskCompositerSelector = createMemoizedSelector( export const canvasMaskCompositerSelector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
selectCanvasSlice, return {
(canvas) => { stageCoordinates: canvas.stageCoordinates,
return { stageDimensions: canvas.stageDimensions,
stageCoordinates: canvas.stageCoordinates, };
stageDimensions: canvas.stageDimensions, });
};
}
);
type IAICanvasMaskCompositerProps = RectConfig; type IAICanvasMaskCompositerProps = RectConfig;
const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => { const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
const { ...rest } = props; const { ...rest } = props;
const { stageCoordinates, stageDimensions } = useAppSelector( const { stageCoordinates, stageDimensions } = useAppSelector(canvasMaskCompositerSelector);
canvasMaskCompositerSelector
);
const stageScale = useAppSelector((s) => s.canvas.stageScale); const stageScale = useAppSelector((s) => s.canvas.stageScale);
const maskColorString = useAppSelector((s) => const maskColorString = useAppSelector((s) => rgbaColorToString(s.canvas.maskColor));
rgbaColorToString(s.canvas.maskColor) const [fillPatternImage, setFillPatternImage] = useState<HTMLImageElement | null>(null);
);
const [fillPatternImage, setFillPatternImage] =
useState<HTMLImageElement | null>(null);
const [offset, setOffset] = useState<number>(0); const [offset, setOffset] = useState<number>(0);
@ -66,10 +58,7 @@ const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
return () => clearInterval(timer); return () => clearInterval(timer);
}, []); }, []);
const fillPatternScale = useMemo( const fillPatternScale = useMemo(() => ({ x: 1 / stageScale, y: 1 / stageScale }), [stageScale]);
() => ({ x: 1 / stageScale, y: 1 / stageScale }),
[stageScale]
);
if ( if (
!fillPatternImage || !fillPatternImage ||

View File

@ -27,9 +27,7 @@ const IAICanvasLines = (props: InpaintingCanvasLinesProps) => {
lineJoin="round" lineJoin="round"
shadowForStrokeEnabled={false} shadowForStrokeEnabled={false}
listening={false} listening={false}
globalCompositeOperation={ globalCompositeOperation={line.tool === 'brush' ? 'source-over' : 'destination-out'}
line.tool === 'brush' ? 'source-over' : 'destination-out'
}
/> />
))} ))}
</Group> </Group>

Some files were not shown because too many files have changed in this diff Show More