mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-01-19 01:57:55 -05:00
Compare commits
19 Commits
v4.2.9.dev
...
v4.2.9.dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
434afdd21d | ||
|
|
ea267c0e09 | ||
|
|
35b483b83a | ||
|
|
217b2759c3 | ||
|
|
7d528dc03d | ||
|
|
bc40c1a99f | ||
|
|
a2b508016e | ||
|
|
3ce8294379 | ||
|
|
9c3da8de8e | ||
|
|
ccba597e58 | ||
|
|
8028943cdd | ||
|
|
cdd8b60fd0 | ||
|
|
c624754404 | ||
|
|
0505634296 | ||
|
|
e06ea5d595 | ||
|
|
838a0574a5 | ||
|
|
ed3581c70c | ||
|
|
522cae6a42 | ||
|
|
a9032a34f2 |
@@ -1,5 +1,5 @@
|
||||
import { PropsWithChildren, memo, useEffect } from 'react';
|
||||
import { modelChanged } from '../src/features/controlLayers/store/canvasV2Slice';
|
||||
import { modelChanged } from '../src/features/controlLayers/store/paramsSlice';
|
||||
import { useAppDispatch } from '../src/app/store/storeHooks';
|
||||
import { useGlobalModifiersInit } from '@invoke-ai/ui-library';
|
||||
/**
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"@dnd-kit/sortable": "^8.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@fontsource-variable/inter": "^5.0.20",
|
||||
"@invoke-ai/ui-library": "^0.0.31",
|
||||
"@invoke-ai/ui-library": "^0.0.32",
|
||||
"@nanostores/react": "^0.7.3",
|
||||
"@reduxjs/toolkit": "2.2.3",
|
||||
"@roarr/browser-log-writer": "^1.3.0",
|
||||
|
||||
203
invokeai/frontend/web/pnpm-lock.yaml
generated
203
invokeai/frontend/web/pnpm-lock.yaml
generated
@@ -24,8 +24,8 @@ dependencies:
|
||||
specifier: ^5.0.20
|
||||
version: 5.0.20
|
||||
'@invoke-ai/ui-library':
|
||||
specifier: ^0.0.31
|
||||
version: 0.0.31(@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.20)(@types/react@18.3.3)(i18next@23.12.2)(react-dom@18.3.1)(react@18.3.1)
|
||||
specifier: ^0.0.32
|
||||
version: 0.0.32(@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.20)(@types/react@18.3.3)(i18next@23.12.2)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@nanostores/react':
|
||||
specifier: ^0.7.3
|
||||
version: 0.7.3(nanostores@0.11.2)(react@18.3.1)
|
||||
@@ -40,7 +40,7 @@ dependencies:
|
||||
version: 0.5.0
|
||||
chakra-react-select:
|
||||
specifier: ^4.9.1
|
||||
version: 4.9.1(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@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)(@emotion/react@11.13.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
||||
version: 4.9.1(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@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)(@emotion/react@11.13.3)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
||||
compare-versions:
|
||||
specifier: ^6.1.1
|
||||
version: 6.1.1
|
||||
@@ -1752,6 +1752,13 @@ packages:
|
||||
dependencies:
|
||||
regenerator-runtime: 0.14.1
|
||||
|
||||
/@babel/runtime@7.25.4:
|
||||
resolution: {integrity: sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
regenerator-runtime: 0.14.1
|
||||
dev: false
|
||||
|
||||
/@babel/template@7.24.0:
|
||||
resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -1838,7 +1845,7 @@ packages:
|
||||
'@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.3.1)
|
||||
framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
@@ -1854,7 +1861,7 @@ packages:
|
||||
'@chakra-ui/react-context': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -1872,7 +1879,7 @@ packages:
|
||||
'@chakra-ui/react-children-utils': 2.0.6(react@18.3.1)
|
||||
'@chakra-ui/react-context': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -1885,7 +1892,7 @@ packages:
|
||||
'@chakra-ui/react-children-utils': 2.0.6(react@18.3.1)
|
||||
'@chakra-ui/react-context': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -1905,7 +1912,7 @@ packages:
|
||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -1916,7 +1923,7 @@ packages:
|
||||
react: '>=18'
|
||||
dependencies:
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -1935,7 +1942,7 @@ packages:
|
||||
'@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@zag-js/focus-visible': 0.16.0
|
||||
react: 18.3.1
|
||||
@@ -1958,7 +1965,7 @@ packages:
|
||||
react: '>=18'
|
||||
dependencies:
|
||||
'@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -1977,7 +1984,7 @@ packages:
|
||||
'@chakra-ui/system': '>=2.0.0'
|
||||
react: '>=18'
|
||||
dependencies:
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -1992,13 +1999,13 @@ packages:
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
/@chakra-ui/css-reset@2.3.0(@emotion/react@11.13.0)(react@18.3.1):
|
||||
/@chakra-ui/css-reset@2.3.0(@emotion/react@11.13.3)(react@18.3.1):
|
||||
resolution: {integrity: sha512-cQwwBy5O0jzvl0K7PLTLgp8ijqLPKyuEMiDXwYzl95seD3AoeuoCLyzZcJtVqaUZ573PiBdAbY/IlZcwDOItWg==}
|
||||
peerDependencies:
|
||||
'@emotion/react': '>=10.0.35'
|
||||
react: '>=18'
|
||||
dependencies:
|
||||
'@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2031,7 +2038,7 @@ packages:
|
||||
'@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2062,7 +2069,7 @@ packages:
|
||||
'@chakra-ui/react-types': 2.0.7(react@18.3.1)
|
||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2085,7 +2092,7 @@ packages:
|
||||
react: '>=18'
|
||||
dependencies:
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2096,7 +2103,7 @@ packages:
|
||||
react: '>=18'
|
||||
dependencies:
|
||||
'@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2108,7 +2115,7 @@ packages:
|
||||
dependencies:
|
||||
'@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2123,7 +2130,7 @@ packages:
|
||||
'@chakra-ui/react-children-utils': 2.0.6(react@18.3.1)
|
||||
'@chakra-ui/react-context': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2139,7 +2146,7 @@ packages:
|
||||
'@chakra-ui/react-children-utils': 2.0.6(react@18.3.1)
|
||||
'@chakra-ui/react-context': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2164,7 +2171,7 @@ packages:
|
||||
'@chakra-ui/breakpoint-utils': 2.0.8
|
||||
'@chakra-ui/react-env': 3.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2189,7 +2196,7 @@ packages:
|
||||
'@chakra-ui/react-use-outside-click': 2.2.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.3.1)
|
||||
framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
@@ -2216,7 +2223,7 @@ packages:
|
||||
'@chakra-ui/react-use-outside-click': 2.2.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/transition': 2.1.0(framer-motion@11.3.24)(react@18.3.1)
|
||||
framer-motion: 11.3.24(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
@@ -2237,7 +2244,7 @@ packages:
|
||||
'@chakra-ui/react-types': 2.0.7(react@18.3.1)
|
||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.3.1)
|
||||
aria-hidden: 1.2.4
|
||||
framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1)
|
||||
@@ -2266,7 +2273,7 @@ packages:
|
||||
'@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2290,7 +2297,7 @@ packages:
|
||||
'@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2312,7 +2319,7 @@ packages:
|
||||
'@chakra-ui/react-use-focus-on-pointer-down': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
@@ -2347,11 +2354,11 @@ packages:
|
||||
react: '>=18'
|
||||
dependencies:
|
||||
'@chakra-ui/react-context': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
/@chakra-ui/provider@2.4.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react-dom@18.3.1)(react@18.3.1):
|
||||
/@chakra-ui/provider@2.4.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-w0Tef5ZCJK1mlJorcSjItCSbyvVuqpvyWdxZiVQmE6fvSJR83wZof42ux0+sfWD+I7rHSfj+f9nzhNaEWClysw==}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.0.0
|
||||
@@ -2359,13 +2366,13 @@ packages:
|
||||
react: '>=18'
|
||||
react-dom: '>=18'
|
||||
dependencies:
|
||||
'@chakra-ui/css-reset': 2.3.0(@emotion/react@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/css-reset': 2.3.0(@emotion/react@11.13.3)(react@18.3.1)
|
||||
'@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1)
|
||||
'@chakra-ui/react-env': 3.1.0(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/utils': 2.0.15
|
||||
'@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/styled': 11.13.0(@emotion/react@11.13.0)(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.3)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
dev: false
|
||||
@@ -2381,7 +2388,7 @@ packages:
|
||||
'@chakra-ui/react-types': 2.0.7(react@18.3.1)
|
||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@zag-js/focus-visible': 0.16.0
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
@@ -2581,7 +2588,7 @@ packages:
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
/@chakra-ui/react@2.8.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(@types/react@18.3.3)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1):
|
||||
/@chakra-ui/react@2.8.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.3)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.0.0
|
||||
@@ -2600,7 +2607,7 @@ packages:
|
||||
'@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/control-box': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/counter': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/css-reset': 2.3.0(@emotion/react@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/css-reset': 2.3.0(@emotion/react@11.13.3)(react@18.3.1)
|
||||
'@chakra-ui/editable': 3.1.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/focus-lock': 2.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||
'@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
@@ -2619,7 +2626,7 @@ packages:
|
||||
'@chakra-ui/popper': 3.1.0(react@18.3.1)
|
||||
'@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1)
|
||||
'@chakra-ui/progress': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/provider': 2.4.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@chakra-ui/provider': 2.4.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@chakra-ui/radio': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/react-env': 3.1.0(react@18.3.1)
|
||||
'@chakra-ui/select': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
@@ -2631,7 +2638,7 @@ packages:
|
||||
'@chakra-ui/stepper': 2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/styled-system': 2.9.2
|
||||
'@chakra-ui/switch': 2.1.2(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/table': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/tabs': 3.0.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/tag': 3.1.1(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
@@ -2643,8 +2650,8 @@ packages:
|
||||
'@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.3.1)
|
||||
'@chakra-ui/utils': 2.0.15
|
||||
'@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/styled': 11.13.0(@emotion/react@11.13.0)(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.3)(react@18.3.1)
|
||||
framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@@ -2660,7 +2667,7 @@ packages:
|
||||
dependencies:
|
||||
'@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2677,7 +2684,7 @@ packages:
|
||||
'@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/react-use-previous': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2687,7 +2694,7 @@ packages:
|
||||
'@chakra-ui/system': '>=2.0.0'
|
||||
react: '>=18'
|
||||
dependencies:
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2707,7 +2714,7 @@ packages:
|
||||
'@chakra-ui/react-use-pan-event': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-size': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2718,7 +2725,7 @@ packages:
|
||||
react: '>=18'
|
||||
dependencies:
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2731,7 +2738,7 @@ packages:
|
||||
'@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/react-context': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2744,7 +2751,7 @@ packages:
|
||||
'@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/react-context': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2765,12 +2772,12 @@ packages:
|
||||
dependencies:
|
||||
'@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
/@chakra-ui/system@2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1):
|
||||
/@chakra-ui/system@2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1):
|
||||
resolution: {integrity: sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.0.0
|
||||
@@ -2783,8 +2790,8 @@ packages:
|
||||
'@chakra-ui/styled-system': 2.9.2
|
||||
'@chakra-ui/theme-utils': 2.0.21
|
||||
'@chakra-ui/utils': 2.0.15
|
||||
'@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/styled': 11.13.0(@emotion/react@11.13.0)(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.3)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-fast-compare: 3.2.2
|
||||
dev: false
|
||||
@@ -2797,7 +2804,7 @@ packages:
|
||||
dependencies:
|
||||
'@chakra-ui/react-context': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2816,7 +2823,7 @@ packages:
|
||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2828,7 +2835,7 @@ packages:
|
||||
dependencies:
|
||||
'@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/react-context': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2840,7 +2847,7 @@ packages:
|
||||
dependencies:
|
||||
'@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -2891,7 +2898,7 @@ packages:
|
||||
'@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/styled-system': 2.9.2
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2)
|
||||
framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
@@ -2914,7 +2921,7 @@ packages:
|
||||
'@chakra-ui/react-use-event-listener': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1)
|
||||
'@chakra-ui/shared-utils': 2.0.5
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@@ -2957,7 +2964,7 @@ packages:
|
||||
'@chakra-ui/system': '>=2.0.0'
|
||||
react: '>=18'
|
||||
dependencies:
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -3040,10 +3047,10 @@ packages:
|
||||
resolution: {integrity: sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==}
|
||||
dependencies:
|
||||
'@babel/helper-module-imports': 7.24.7
|
||||
'@babel/runtime': 7.25.0
|
||||
'@babel/runtime': 7.25.4
|
||||
'@emotion/hash': 0.9.2
|
||||
'@emotion/memoize': 0.9.0
|
||||
'@emotion/serialize': 1.3.0
|
||||
'@emotion/serialize': 1.3.1
|
||||
babel-plugin-macros: 3.1.0
|
||||
convert-source-map: 1.9.0
|
||||
escape-string-regexp: 4.0.0
|
||||
@@ -3131,8 +3138,8 @@ packages:
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
/@emotion/react@11.13.0(@types/react@18.3.3)(react@18.3.1):
|
||||
resolution: {integrity: sha512-WkL+bw1REC2VNV1goQyfxjx1GYJkcc23CRQkXX+vZNLINyfI7o+uUn/rTGPt/xJ3bJHd5GcljgnxHf4wRw5VWQ==}
|
||||
/@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1):
|
||||
resolution: {integrity: sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: '>=16.8.0'
|
||||
@@ -3140,10 +3147,10 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.25.0
|
||||
'@babel/runtime': 7.25.4
|
||||
'@emotion/babel-plugin': 11.12.0
|
||||
'@emotion/cache': 11.13.1
|
||||
'@emotion/serialize': 1.3.0
|
||||
'@emotion/serialize': 1.3.1
|
||||
'@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1)
|
||||
'@emotion/utils': 1.4.0
|
||||
'@emotion/weak-memoize': 0.4.0
|
||||
@@ -3164,12 +3171,12 @@ packages:
|
||||
csstype: 3.1.3
|
||||
dev: false
|
||||
|
||||
/@emotion/serialize@1.3.0:
|
||||
resolution: {integrity: sha512-jACuBa9SlYajnpIVXB+XOXnfJHyckDfe6fOpORIM6yhBDlqGuExvDdZYHDQGoDf3bZXGv7tNr+LpLjJqiEQ6EA==}
|
||||
/@emotion/serialize@1.3.1:
|
||||
resolution: {integrity: sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==}
|
||||
dependencies:
|
||||
'@emotion/hash': 0.9.2
|
||||
'@emotion/memoize': 0.9.0
|
||||
'@emotion/unitless': 0.9.0
|
||||
'@emotion/unitless': 0.10.0
|
||||
'@emotion/utils': 1.4.0
|
||||
csstype: 3.1.3
|
||||
dev: false
|
||||
@@ -3182,7 +3189,7 @@ packages:
|
||||
resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==}
|
||||
dev: false
|
||||
|
||||
/@emotion/styled@11.13.0(@emotion/react@11.13.0)(@types/react@18.3.3)(react@18.3.1):
|
||||
/@emotion/styled@11.13.0(@emotion/react@11.13.3)(@types/react@18.3.3)(react@18.3.1):
|
||||
resolution: {integrity: sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.0.0-rc.0
|
||||
@@ -3192,11 +3199,11 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.25.0
|
||||
'@babel/runtime': 7.25.4
|
||||
'@emotion/babel-plugin': 11.12.0
|
||||
'@emotion/is-prop-valid': 1.3.0
|
||||
'@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/serialize': 1.3.0
|
||||
'@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/serialize': 1.3.1
|
||||
'@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1)
|
||||
'@emotion/utils': 1.4.0
|
||||
'@types/react': 18.3.3
|
||||
@@ -3205,12 +3212,12 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@emotion/unitless@0.8.1:
|
||||
resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
|
||||
/@emotion/unitless@0.10.0:
|
||||
resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==}
|
||||
dev: false
|
||||
|
||||
/@emotion/unitless@0.9.0:
|
||||
resolution: {integrity: sha512-TP6GgNZtmtFaFcsOgExdnfxLLpRDla4Q66tnenA9CktvVSdNKDvMVuUah4QvWPIpNjrWsGg3qeGo9a43QooGZQ==}
|
||||
/@emotion/unitless@0.8.1:
|
||||
resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
|
||||
dev: false
|
||||
|
||||
/@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.3.1):
|
||||
@@ -3564,8 +3571,8 @@ packages:
|
||||
prettier: 3.3.3
|
||||
dev: true
|
||||
|
||||
/@invoke-ai/ui-library@0.0.31(@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.20)(@types/react@18.3.3)(i18next@23.12.2)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-7LtOUN/bcGHc8jCRd2m22DvP2eeogqwM/shdXQpLH5RY2FzWJNXlWdVT4hIPGDu7znnk3xvXlZvo6tiGSjbnCQ==}
|
||||
/@invoke-ai/ui-library@0.0.32(@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.20)(@types/react@18.3.3)(i18next@23.12.2)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-JxAoblrDu/cZ4ha9KO4ry5OWvyLUE1Dj28i+ciMaDNUpC/cN+IyiTbUBoFoPaoN5JP8Zpd/MYCcmF2qsziHDzg==}
|
||||
peerDependencies:
|
||||
'@fontsource-variable/inter': ^5.0.16
|
||||
react: ^18.2.0
|
||||
@@ -3575,14 +3582,14 @@ packages:
|
||||
'@chakra-ui/icons': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1)
|
||||
'@chakra-ui/react': 2.8.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(@types/react@18.3.3)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@chakra-ui/react': 2.8.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.3)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@chakra-ui/styled-system': 2.9.2
|
||||
'@chakra-ui/theme-tools': 2.1.2(@chakra-ui/styled-system@2.9.2)
|
||||
'@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/styled': 11.13.0(@emotion/react@11.13.0)(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1)
|
||||
'@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.3)(react@18.3.1)
|
||||
'@fontsource-variable/inter': 5.0.20
|
||||
'@nanostores/react': 0.7.3(nanostores@0.11.2)(react@18.3.1)
|
||||
chakra-react-select: 4.9.1(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@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)(@emotion/react@11.13.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
||||
chakra-react-select: 4.9.1(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@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)(@emotion/react@11.13.3)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
||||
framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1)
|
||||
lodash-es: 4.17.21
|
||||
nanostores: 0.11.2
|
||||
@@ -5774,7 +5781,7 @@ packages:
|
||||
resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
tslib: 2.6.3
|
||||
tslib: 2.7.0
|
||||
dev: false
|
||||
|
||||
/aria-query@5.3.0:
|
||||
@@ -6126,7 +6133,7 @@ packages:
|
||||
type-detect: 4.0.8
|
||||
dev: true
|
||||
|
||||
/chakra-react-select@4.9.1(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@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)(@emotion/react@11.13.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
||||
/chakra-react-select@4.9.1(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@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)(@emotion/react@11.13.3)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-jmgfN+S/wnTaCp3pW30GYDIZ5J8jWcT1gIbhpw6RdKV+atm/U4/sT+gaHOHHhRL8xeaYip+iI/m8MPGREkve0w==}
|
||||
peerDependencies:
|
||||
'@chakra-ui/form-control': ^2.0.0
|
||||
@@ -6146,8 +6153,8 @@ packages:
|
||||
'@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.3.24)(react@18.3.1)
|
||||
'@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.0)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@emotion/react': 11.13.0(@types/react@18.3.3)(react@18.3.1)
|
||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)
|
||||
'@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-select: 5.8.0(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)
|
||||
@@ -7572,7 +7579,7 @@ packages:
|
||||
resolution: {integrity: sha512-QFaHbhv9WPUeLYBDe/PAuLKJ4Dd9OPvKs9xZBr3yLXnUrDNaVXKu2baDBXe3naPY30hgHYSsf2JW4jzas2mDEQ==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
tslib: 2.6.3
|
||||
tslib: 2.7.0
|
||||
dev: false
|
||||
|
||||
/for-each@0.3.3:
|
||||
@@ -7611,7 +7618,7 @@ packages:
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
tslib: 2.6.3
|
||||
tslib: 2.7.0
|
||||
optionalDependencies:
|
||||
'@emotion/is-prop-valid': 0.8.8
|
||||
dev: false
|
||||
@@ -9619,7 +9626,7 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@babel/runtime': 7.25.0
|
||||
'@babel/runtime': 7.25.4
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
@@ -9714,7 +9721,7 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.25.0
|
||||
'@babel/runtime': 7.25.4
|
||||
'@types/react': 18.3.3
|
||||
focus-lock: 1.3.5
|
||||
prop-types: 15.8.1
|
||||
@@ -9776,7 +9783,7 @@ packages:
|
||||
react-native:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.25.0
|
||||
'@babel/runtime': 7.25.4
|
||||
html-parse-stringify: 3.0.1
|
||||
i18next: 23.12.2
|
||||
react: 18.3.1
|
||||
@@ -9838,7 +9845,7 @@ packages:
|
||||
'@types/react': 18.3.3
|
||||
react: 18.3.1
|
||||
react-style-singleton: 2.2.1(@types/react@18.3.3)(react@18.3.1)
|
||||
tslib: 2.6.3
|
||||
tslib: 2.7.0
|
||||
dev: false
|
||||
|
||||
/react-remove-scroll@2.5.10(@types/react@18.3.3)(react@18.3.1):
|
||||
@@ -9855,7 +9862,7 @@ packages:
|
||||
react: 18.3.1
|
||||
react-remove-scroll-bar: 2.3.6(@types/react@18.3.3)(react@18.3.1)
|
||||
react-style-singleton: 2.2.1(@types/react@18.3.3)(react@18.3.1)
|
||||
tslib: 2.6.3
|
||||
tslib: 2.7.0
|
||||
use-callback-ref: 1.3.2(@types/react@18.3.3)(react@18.3.1)
|
||||
use-sidecar: 1.1.2(@types/react@18.3.3)(react@18.3.1)
|
||||
dev: false
|
||||
@@ -9905,7 +9912,7 @@ packages:
|
||||
get-nonce: 1.0.1
|
||||
invariant: 2.2.4
|
||||
react: 18.3.1
|
||||
tslib: 2.6.3
|
||||
tslib: 2.7.0
|
||||
dev: false
|
||||
|
||||
/react-transition-group@4.4.5(react-dom@18.3.1)(react@18.3.1):
|
||||
@@ -11045,6 +11052,10 @@ packages:
|
||||
/tslib@2.6.3:
|
||||
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
|
||||
|
||||
/tslib@2.7.0:
|
||||
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
|
||||
dev: false
|
||||
|
||||
/tsutils@3.21.0(typescript@5.5.4):
|
||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -11292,7 +11303,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/react': 18.3.3
|
||||
react: 18.3.1
|
||||
tslib: 2.6.3
|
||||
tslib: 2.7.0
|
||||
dev: false
|
||||
|
||||
/use-debounce@10.0.2(react@18.3.1):
|
||||
@@ -11338,7 +11349,7 @@ packages:
|
||||
'@types/react': 18.3.3
|
||||
detect-node-es: 1.1.0
|
||||
react: 18.3.1
|
||||
tslib: 2.6.3
|
||||
tslib: 2.7.0
|
||||
dev: false
|
||||
|
||||
/use-sync-external-store@1.2.0(react@18.3.1):
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import {
|
||||
rasterLayerAdded,
|
||||
sessionStagingAreaImageAccepted,
|
||||
sessionStagingAreaReset,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
} from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { rasterLayerAdded } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import type { CanvasRasterLayerState } from 'features/controlLayers/store/types';
|
||||
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
|
||||
import { toast } from 'features/toast/toast';
|
||||
@@ -55,10 +55,10 @@ export const addStagingListeners = (startAppListening: AppStartListening) => {
|
||||
effect: (action, api) => {
|
||||
const { index } = action.payload;
|
||||
const state = api.getState();
|
||||
const stagingAreaImage = state.canvasV2.session.stagedImages[index];
|
||||
const stagingAreaImage = state.canvasSession.stagedImages[index];
|
||||
|
||||
assert(stagingAreaImage, 'No staged image found to accept');
|
||||
const { x, y } = state.canvasV2.bbox.rect;
|
||||
const { x, y } = state.canvasV2.present.bbox.rect;
|
||||
|
||||
const { imageDTO, offsetX, offsetY } = stagingAreaImage;
|
||||
const imageObject = imageDTOToImageObject(imageDTO);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { setInfillMethod } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { setInfillMethod } from 'features/controlLayers/store/paramsSlice';
|
||||
import { shouldUseNSFWCheckerChanged, shouldUseWatermarkerChanged } from 'features/system/store/systemSlice';
|
||||
import { appInfoApi } from 'services/api/endpoints/appInfo';
|
||||
|
||||
@@ -8,7 +8,7 @@ export const addAppConfigReceivedListener = (startAppListening: AppStartListenin
|
||||
matcher: appInfoApi.endpoints.getAppConfig.matchFulfilled,
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
const { infill_methods = [], nsfw_methods = [], watermarking_methods = [] } = action.payload;
|
||||
const infillMethod = getState().canvasV2.compositing.infillMethod;
|
||||
const infillMethod = getState().params.infillMethod;
|
||||
|
||||
if (!infill_methods.includes(infillMethod)) {
|
||||
// if there is no infill method, set it to the first one
|
||||
|
||||
@@ -16,7 +16,7 @@ export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppS
|
||||
const { nodes, canvasV2 } = getState();
|
||||
|
||||
deleted_images.forEach((image_name) => {
|
||||
const imageUsage = getImageUsage(nodes.present, canvasV2, image_name);
|
||||
const imageUsage = getImageUsage(nodes.present, canvasV2.present, image_name);
|
||||
|
||||
if (imageUsage.isNodesImage && !wasNodeEditorReset) {
|
||||
dispatch(nodeEditorReset());
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { SerializableObject } from 'common/types';
|
||||
import type { Result } from 'common/util/result';
|
||||
import { isErr, withResult, withResultAsync } from 'common/util/result';
|
||||
import { $canvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { sessionStagingAreaReset, sessionStartedStaging } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { sessionStagingAreaReset, sessionStartedStaging } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
|
||||
import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph';
|
||||
import { buildSDXLGraph } from 'features/nodes/util/graph/generation/buildSDXLGraph';
|
||||
@@ -23,7 +23,7 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
|
||||
enqueueRequested.match(action) && action.payload.tabName === 'generation',
|
||||
effect: async (action, { getState, dispatch }) => {
|
||||
const state = getState();
|
||||
const model = state.canvasV2.params.model;
|
||||
const model = state.params.model;
|
||||
const { prepend } = action.payload;
|
||||
|
||||
const manager = $canvasManager.get();
|
||||
@@ -31,13 +31,13 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
|
||||
|
||||
let didStartStaging = false;
|
||||
|
||||
if (!state.canvasV2.session.isStaging && state.canvasV2.session.mode === 'compose') {
|
||||
if (!state.canvasSession.isStaging && state.canvasSession.mode === 'compose') {
|
||||
dispatch(sessionStartedStaging());
|
||||
didStartStaging = true;
|
||||
}
|
||||
|
||||
const abortStaging = () => {
|
||||
if (didStartStaging && getState().canvasV2.session.isStaging) {
|
||||
if (didStartStaging && getState().canvasSession.isStaging) {
|
||||
dispatch(sessionStagingAreaReset());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) =
|
||||
batch: {
|
||||
graph,
|
||||
workflow: builtWorkflow,
|
||||
runs: state.canvasV2.params.iterations,
|
||||
runs: state.params.iterations,
|
||||
origin: 'workflows',
|
||||
},
|
||||
prepend: action.payload.prepend,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import type { AppDispatch, RootState } from 'app/store/store';
|
||||
import { entityDeleted, ipaImageChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
||||
import { isModalOpenChanged } from 'features/deleteImageModal/store/slice';
|
||||
import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors';
|
||||
@@ -39,7 +40,7 @@ const deleteNodesImages = (state: RootState, dispatch: AppDispatch, imageDTO: Im
|
||||
};
|
||||
|
||||
// const deleteControlAdapterImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||
// state.canvasV2.controlAdapters.entities.forEach(({ id, imageObject, processedImageObject }) => {
|
||||
// state.canvasV2.present.controlAdapters.entities.forEach(({ id, imageObject, processedImageObject }) => {
|
||||
// if (
|
||||
// imageObject?.image.image_name === imageDTO.image_name ||
|
||||
// processedImageObject?.image.image_name === imageDTO.image_name
|
||||
@@ -51,15 +52,15 @@ const deleteNodesImages = (state: RootState, dispatch: AppDispatch, imageDTO: Im
|
||||
// };
|
||||
|
||||
const deleteIPAdapterImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||
state.canvasV2.ipAdapters.entities.forEach(({ id, ipAdapter }) => {
|
||||
if (ipAdapter.image?.image_name === imageDTO.image_name) {
|
||||
dispatch(ipaImageChanged({ id, imageDTO: null }));
|
||||
state.canvasV2.present.ipAdapters.entities.forEach((entity) => {
|
||||
if (entity.ipAdapter.image?.image_name === imageDTO.image_name) {
|
||||
dispatch(ipaImageChanged({ entityIdentifier: getEntityIdentifier(entity), imageDTO: null }));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const deleteLayerImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||
state.canvasV2.rasterLayers.entities.forEach(({ id, objects }) => {
|
||||
state.canvasV2.present.rasterLayers.entities.forEach(({ id, objects }) => {
|
||||
let shouldDelete = false;
|
||||
for (const obj of objects) {
|
||||
if (obj.type === 'image' && obj.image.image_name === imageDTO.image_name) {
|
||||
|
||||
@@ -51,7 +51,9 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
||||
activeData.payload.imageDTO
|
||||
) {
|
||||
const { id } = overData.context;
|
||||
dispatch(ipaImageChanged({ id, imageDTO: activeData.payload.imageDTO }));
|
||||
dispatch(
|
||||
ipaImageChanged({ entityIdentifier: { id, type: 'ip_adapter' }, imageDTO: activeData.payload.imageDTO })
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -64,7 +66,13 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
||||
activeData.payload.imageDTO
|
||||
) {
|
||||
const { id, ipAdapterId } = overData.context;
|
||||
dispatch(rgIPAdapterImageChanged({ id, ipAdapterId, imageDTO: activeData.payload.imageDTO }));
|
||||
dispatch(
|
||||
rgIPAdapterImageChanged({
|
||||
entityIdentifier: { id, type: 'regional_guidance' },
|
||||
ipAdapterId,
|
||||
imageDTO: activeData.payload.imageDTO,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -77,7 +85,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
||||
activeData.payload.imageDTO
|
||||
) {
|
||||
const imageObject = imageDTOToImageObject(activeData.payload.imageDTO);
|
||||
const { x, y } = getState().canvasV2.bbox.rect;
|
||||
const { x, y } = getState().canvasV2.present.bbox.rect;
|
||||
const overrides: Partial<CanvasRasterLayerState> = {
|
||||
objects: [imageObject],
|
||||
position: { x, y },
|
||||
@@ -95,7 +103,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
||||
activeData.payload.imageDTO
|
||||
) {
|
||||
const imageObject = imageDTOToImageObject(activeData.payload.imageDTO);
|
||||
const { x, y } = getState().canvasV2.bbox.rect;
|
||||
const { x, y } = getState().canvasV2.present.bbox.rect;
|
||||
const overrides: Partial<CanvasControlLayerState> = {
|
||||
objects: [imageObject],
|
||||
position: { x, y },
|
||||
|
||||
@@ -89,14 +89,16 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
||||
|
||||
if (postUploadAction?.type === 'SET_IPA_IMAGE') {
|
||||
const { id } = postUploadAction;
|
||||
dispatch(ipaImageChanged({ id, imageDTO }));
|
||||
dispatch(ipaImageChanged({ entityIdentifier: { id, type: 'ip_adapter' }, imageDTO }));
|
||||
toast({ ...DEFAULT_UPLOADED_TOAST, description: t('toast.setControlImage') });
|
||||
return;
|
||||
}
|
||||
|
||||
if (postUploadAction?.type === 'SET_RG_IP_ADAPTER_IMAGE') {
|
||||
const { id, ipAdapterId } = postUploadAction;
|
||||
dispatch(rgIPAdapterImageChanged({ id, ipAdapterId, imageDTO }));
|
||||
dispatch(
|
||||
rgIPAdapterImageChanged({ entityIdentifier: { id, type: 'regional_guidance' }, ipAdapterId, imageDTO })
|
||||
);
|
||||
toast({ ...DEFAULT_UPLOADED_TOAST, description: t('toast.setControlImage') });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { loraDeleted, modelChanged, vaeSelected } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { loraDeleted } from 'features/controlLayers/store/lorasSlice';
|
||||
import { modelChanged, vaeSelected } from 'features/controlLayers/store/paramsSlice';
|
||||
import { modelSelected } from 'features/parameters/store/actions';
|
||||
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
|
||||
import { toast } from 'features/toast/toast';
|
||||
@@ -23,14 +24,14 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
|
||||
const newModel = result.data;
|
||||
|
||||
const newBaseModel = newModel.base;
|
||||
const didBaseModelChange = state.canvasV2.params.model?.base !== newBaseModel;
|
||||
const didBaseModelChange = state.params.model?.base !== newBaseModel;
|
||||
|
||||
if (didBaseModelChange) {
|
||||
// we may need to reset some incompatible submodels
|
||||
let modelsCleared = 0;
|
||||
|
||||
// handle incompatible loras
|
||||
state.canvasV2.loras.forEach((lora) => {
|
||||
state.loras.loras.forEach((lora) => {
|
||||
if (lora.model.base !== newBaseModel) {
|
||||
dispatch(loraDeleted({ id: lora.id }));
|
||||
modelsCleared += 1;
|
||||
@@ -38,14 +39,14 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
|
||||
});
|
||||
|
||||
// handle incompatible vae
|
||||
const { vae } = state.canvasV2.params;
|
||||
const { vae } = state.params;
|
||||
if (vae && vae.base !== newBaseModel) {
|
||||
dispatch(vaeSelected(null));
|
||||
modelsCleared += 1;
|
||||
}
|
||||
|
||||
// handle incompatible controlnets
|
||||
// state.canvasV2.controlAdapters.entities.forEach((ca) => {
|
||||
// state.canvasV2.present.controlAdapters.entities.forEach((ca) => {
|
||||
// if (ca.model?.base !== newBaseModel) {
|
||||
// modelsCleared += 1;
|
||||
// if (ca.isEnabled) {
|
||||
@@ -66,7 +67,7 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(modelChanged({ model: newModel, previousModel: state.canvasV2.params.model }));
|
||||
dispatch(modelChanged({ model: newModel, previousModel: state.params.model }));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -7,12 +7,11 @@ import {
|
||||
bboxWidthChanged,
|
||||
controlLayerModelChanged,
|
||||
ipaModelChanged,
|
||||
loraDeleted,
|
||||
modelChanged,
|
||||
refinerModelChanged,
|
||||
rgIPAdapterModelChanged,
|
||||
vaeSelected,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { loraDeleted } from 'features/controlLayers/store/lorasSlice';
|
||||
import { modelChanged, refinerModelChanged, vaeSelected } from 'features/controlLayers/store/paramsSlice';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { calculateNewSize } from 'features/parameters/components/DocumentSize/calculateNewSize';
|
||||
import { postProcessingModelChanged, upscaleModelChanged } from 'features/parameters/store/upscaleSlice';
|
||||
import { zParameterModel, zParameterVAEModel } from 'features/parameters/types/parameterSchemas';
|
||||
@@ -62,7 +61,7 @@ type ModelHandler = (
|
||||
) => undefined;
|
||||
|
||||
const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
|
||||
const currentModel = state.canvasV2.params.model;
|
||||
const currentModel = state.params.model;
|
||||
const mainModels = models.filter(isNonRefinerMainModelConfig);
|
||||
if (mainModels.length === 0) {
|
||||
// No models loaded at all
|
||||
@@ -84,11 +83,17 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
|
||||
dispatch(modelChanged({ model: defaultModelInList, previousModel: currentModel }));
|
||||
|
||||
const optimalDimension = getOptimalDimension(defaultModelInList);
|
||||
if (getIsSizeOptimal(state.canvasV2.bbox.rect.width, state.canvasV2.bbox.rect.height, optimalDimension)) {
|
||||
if (
|
||||
getIsSizeOptimal(
|
||||
state.canvasV2.present.bbox.rect.width,
|
||||
state.canvasV2.present.bbox.rect.height,
|
||||
optimalDimension
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const { width, height } = calculateNewSize(
|
||||
state.canvasV2.bbox.aspectRatio.value,
|
||||
state.canvasV2.present.bbox.aspectRatio.value,
|
||||
optimalDimension * optimalDimension
|
||||
);
|
||||
|
||||
@@ -109,7 +114,7 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
|
||||
};
|
||||
|
||||
const handleRefinerModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
const currentRefinerModel = state.canvasV2.params.refinerModel;
|
||||
const currentRefinerModel = state.params.refinerModel;
|
||||
const refinerModels = models.filter(isRefinerMainModelModelConfig);
|
||||
if (models.length === 0) {
|
||||
// No models loaded at all
|
||||
@@ -128,7 +133,7 @@ const handleRefinerModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
};
|
||||
|
||||
const handleVAEModels: ModelHandler = (models, state, dispatch, log) => {
|
||||
const currentVae = state.canvasV2.params.vae;
|
||||
const currentVae = state.params.vae;
|
||||
|
||||
if (currentVae === null) {
|
||||
// null is a valid VAE! it means "use the default with the main model"
|
||||
@@ -162,7 +167,7 @@ const handleVAEModels: ModelHandler = (models, state, dispatch, log) => {
|
||||
|
||||
const handleLoRAModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
const loraModels = models.filter(isLoRAModelConfig);
|
||||
state.canvasV2.loras.forEach((lora) => {
|
||||
state.loras.loras.forEach((lora) => {
|
||||
const isLoRAAvailable = loraModels.some((m) => m.key === lora.model.key);
|
||||
if (isLoRAAvailable) {
|
||||
return;
|
||||
@@ -173,32 +178,34 @@ const handleLoRAModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
|
||||
const handleControlAdapterModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
const caModels = models.filter(isControlNetOrT2IAdapterModelConfig);
|
||||
state.canvasV2.controlLayers.entities.forEach((entity) => {
|
||||
state.canvasV2.present.controlLayers.entities.forEach((entity) => {
|
||||
const isModelAvailable = caModels.some((m) => m.key === entity.controlAdapter.model?.key);
|
||||
if (isModelAvailable) {
|
||||
return;
|
||||
}
|
||||
dispatch(controlLayerModelChanged({ id: entity.id, modelConfig: null }));
|
||||
dispatch(controlLayerModelChanged({ entityIdentifier: getEntityIdentifier(entity), modelConfig: null }));
|
||||
});
|
||||
};
|
||||
|
||||
const handleIPAdapterModels: ModelHandler = (models, state, dispatch, _log) => {
|
||||
const ipaModels = models.filter(isIPAdapterModelConfig);
|
||||
state.canvasV2.ipAdapters.entities.forEach((entity) => {
|
||||
state.canvasV2.present.ipAdapters.entities.forEach((entity) => {
|
||||
const isModelAvailable = ipaModels.some((m) => m.key === entity.ipAdapter.model?.key);
|
||||
if (isModelAvailable) {
|
||||
return;
|
||||
}
|
||||
dispatch(ipaModelChanged({ id: entity.id, modelConfig: null }));
|
||||
dispatch(ipaModelChanged({ entityIdentifier: getEntityIdentifier(entity), modelConfig: null }));
|
||||
});
|
||||
|
||||
state.canvasV2.regions.entities.forEach(({ id, ipAdapters }) => {
|
||||
ipAdapters.forEach(({ id: ipAdapterId, model }) => {
|
||||
state.canvasV2.present.regions.entities.forEach((entity) => {
|
||||
entity.ipAdapters.forEach(({ id: ipAdapterId, model }) => {
|
||||
const isModelAvailable = ipaModels.some((m) => m.key === model?.key);
|
||||
if (isModelAvailable) {
|
||||
return;
|
||||
}
|
||||
dispatch(rgIPAdapterModelChanged({ id, ipAdapterId, modelConfig: null }));
|
||||
dispatch(
|
||||
rgIPAdapterModelChanged({ entityIdentifier: getEntityIdentifier(entity), ipAdapterId, modelConfig: null })
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isAnyOf } from '@reduxjs/toolkit';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { positivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { positivePromptChanged } from 'features/controlLayers/store/paramsSlice';
|
||||
import {
|
||||
combinatorialToggled,
|
||||
isErrorChanged,
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { bboxHeightChanged, bboxWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import {
|
||||
bboxHeightChanged,
|
||||
bboxWidthChanged,
|
||||
setCfgRescaleMultiplier,
|
||||
setCfgScale,
|
||||
setScheduler,
|
||||
setSteps,
|
||||
vaePrecisionChanged,
|
||||
vaeSelected,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
} from 'features/controlLayers/store/paramsSlice';
|
||||
import { setDefaultSettings } from 'features/parameters/store/actions';
|
||||
import {
|
||||
isParameterCFGRescaleMultiplier,
|
||||
@@ -31,7 +30,7 @@ export const addSetDefaultSettingsListener = (startAppListening: AppStartListeni
|
||||
effect: async (action, { dispatch, getState }) => {
|
||||
const state = getState();
|
||||
|
||||
const currentModel = state.canvasV2.params.model;
|
||||
const currentModel = state.params.model;
|
||||
|
||||
if (!currentModel) {
|
||||
return;
|
||||
|
||||
@@ -6,7 +6,12 @@ import { errorHandler } from 'app/store/enhancers/reduxRemember/errors';
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice';
|
||||
import { canvasSessionPersistConfig, canvasSessionSlice } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { canvasSettingsPersistConfig, canvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { canvasV2PersistConfig, canvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { lorasPersistConfig, lorasSlice } from 'features/controlLayers/store/lorasSlice';
|
||||
import { paramsPersistConfig, paramsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { toolPersistConfig, toolSlice } from 'features/controlLayers/store/toolSlice';
|
||||
import { deleteImageModalSlice } from 'features/deleteImageModal/store/slice';
|
||||
import { dynamicPromptsPersistConfig, dynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { galleryPersistConfig, gallerySlice } from 'features/gallery/store/gallerySlice';
|
||||
@@ -53,10 +58,15 @@ const allReducers = {
|
||||
[queueSlice.name]: queueSlice.reducer,
|
||||
[workflowSlice.name]: workflowSlice.reducer,
|
||||
[hrfSlice.name]: hrfSlice.reducer,
|
||||
[canvasV2Slice.name]: canvasV2Slice.reducer,
|
||||
[canvasV2Slice.name]: undoable(canvasV2Slice.reducer),
|
||||
[workflowSettingsSlice.name]: workflowSettingsSlice.reducer,
|
||||
[upscaleSlice.name]: upscaleSlice.reducer,
|
||||
[stylePresetSlice.name]: stylePresetSlice.reducer,
|
||||
[paramsSlice.name]: paramsSlice.reducer,
|
||||
[toolSlice.name]: toolSlice.reducer,
|
||||
[canvasSettingsSlice.name]: canvasSettingsSlice.reducer,
|
||||
[canvasSessionSlice.name]: canvasSessionSlice.reducer,
|
||||
[lorasSlice.name]: lorasSlice.reducer,
|
||||
};
|
||||
|
||||
const rootReducer = combineReducers(allReducers);
|
||||
@@ -98,6 +108,11 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
|
||||
[workflowSettingsPersistConfig.name]: workflowSettingsPersistConfig,
|
||||
[upscalePersistConfig.name]: upscalePersistConfig,
|
||||
[stylePresetPersistConfig.name]: stylePresetPersistConfig,
|
||||
[paramsPersistConfig.name]: paramsPersistConfig,
|
||||
[toolPersistConfig.name]: toolPersistConfig,
|
||||
[canvasSettingsPersistConfig.name]: canvasSettingsPersistConfig,
|
||||
[canvasSessionPersistConfig.name]: canvasSessionPersistConfig,
|
||||
[lorasPersistConfig.name]: lorasPersistConfig,
|
||||
};
|
||||
|
||||
const unserialize: UnserializeFunction = (data, key) => {
|
||||
|
||||
@@ -32,7 +32,7 @@ export const useGroupedModelCombobox = <T extends AnyModelConfig>(
|
||||
arg: UseGroupedModelComboboxArg<T>
|
||||
): UseGroupedModelComboboxReturn => {
|
||||
const { t } = useTranslation();
|
||||
const base_model = useAppSelector((s) => s.canvasV2.params.model?.base ?? 'sdxl');
|
||||
const base_model = useAppSelector((s) => s.params.model?.base ?? 'sdxl');
|
||||
const { modelConfigs, selectedModel, getIsDisabled, onChange, isLoading, groupByType = false } = arg;
|
||||
const options = useMemo<GroupBase<ComboboxOption>[]>(() => {
|
||||
if (!modelConfigs) {
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useStore } from '@nanostores/react';
|
||||
import { $isConnected } from 'app/hooks/useSocketIO';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
|
||||
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
@@ -34,13 +35,14 @@ const createSelector = (templates: Templates, isConnected: boolean) =>
|
||||
selectWorkflowSettingsSlice,
|
||||
selectDynamicPromptsSlice,
|
||||
selectCanvasV2Slice,
|
||||
selectParamsSlice,
|
||||
selectUpscalelice,
|
||||
selectConfigSlice,
|
||||
selectActiveTab,
|
||||
],
|
||||
(system, nodes, workflowSettings, dynamicPrompts, canvasV2, upscale, config, activeTabName) => {
|
||||
(system, nodes, workflowSettings, dynamicPrompts, canvasV2, params, upscale, config, activeTabName) => {
|
||||
const { bbox } = canvasV2;
|
||||
const { model, positivePrompt } = canvasV2.params;
|
||||
const { model, positivePrompt } = params;
|
||||
|
||||
const reasons: { prefix?: string; content: string }[] = [];
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { memo } from 'react';
|
||||
export const CanvasEntityList = memo(() => {
|
||||
return (
|
||||
<ScrollableContent>
|
||||
<Flex flexDir="column" gap={4} pt={2} data-testid="control-layers-layer-list">
|
||||
<Flex flexDir="column" gap={4} pt={2} data-testid="control-layers-layer-list" w="full" h="full">
|
||||
<CanvasEntityOpacity />
|
||||
<InpaintMaskList />
|
||||
<RegionalGuidanceEntityList />
|
||||
@@ -0,0 +1,27 @@
|
||||
import { IconButton, Menu, MenuButton, MenuList } from '@invoke-ai/ui-library';
|
||||
import { CanvasEntityListMenuItems } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityListMenuItems';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiDotsThreeOutlineFill } from 'react-icons/pi';
|
||||
|
||||
export const CanvasEntityListMenuButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
aria-label={t('accessibility.menu')}
|
||||
icon={<PiDotsThreeOutlineFill />}
|
||||
variant="link"
|
||||
data-testid="control-layers-add-layer-menu-button"
|
||||
alignSelf="stretch"
|
||||
/>
|
||||
<MenuList>
|
||||
<CanvasEntityListMenuItems />
|
||||
</MenuList>
|
||||
</Menu>
|
||||
);
|
||||
});
|
||||
|
||||
CanvasEntityListMenuButton.displayName = 'CanvasEntityListMenuButton';
|
||||
@@ -0,0 +1,67 @@
|
||||
import { MenuDivider, MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
allEntitiesDeleted,
|
||||
controlLayerAdded,
|
||||
inpaintMaskAdded,
|
||||
ipaAdded,
|
||||
rasterLayerAdded,
|
||||
rgAdded,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntityCount } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiPlusBold, PiTrashSimpleBold } from 'react-icons/pi';
|
||||
|
||||
export const CanvasEntityListMenuItems = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const hasEntities = useAppSelector((s) => {
|
||||
const count = selectEntityCount(s);
|
||||
return count > 0;
|
||||
});
|
||||
const addInpaintMask = useCallback(() => {
|
||||
dispatch(inpaintMaskAdded({ isSelected: true }));
|
||||
}, [dispatch]);
|
||||
const addRegionalGuidance = useCallback(() => {
|
||||
dispatch(rgAdded({ isSelected: true }));
|
||||
}, [dispatch]);
|
||||
const addRasterLayer = useCallback(() => {
|
||||
dispatch(rasterLayerAdded({ isSelected: true }));
|
||||
}, [dispatch]);
|
||||
const addControlLayer = useCallback(() => {
|
||||
dispatch(controlLayerAdded({ isSelected: true }));
|
||||
}, [dispatch]);
|
||||
const addIPAdapter = useCallback(() => {
|
||||
dispatch(ipaAdded({ isSelected: true }));
|
||||
}, [dispatch]);
|
||||
const deleteAll = useCallback(() => {
|
||||
dispatch(allEntitiesDeleted());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuItem icon={<PiPlusBold />} onClick={addInpaintMask}>
|
||||
{t('controlLayers.inpaintMask', { count: 1 })}
|
||||
</MenuItem>
|
||||
<MenuItem icon={<PiPlusBold />} onClick={addRegionalGuidance}>
|
||||
{t('controlLayers.regionalGuidance', { count: 1 })}
|
||||
</MenuItem>
|
||||
<MenuItem icon={<PiPlusBold />} onClick={addRasterLayer}>
|
||||
{t('controlLayers.rasterLayer', { count: 1 })}
|
||||
</MenuItem>
|
||||
<MenuItem icon={<PiPlusBold />} onClick={addControlLayer}>
|
||||
{t('controlLayers.controlLayer', { count: 1 })}
|
||||
</MenuItem>
|
||||
<MenuItem icon={<PiPlusBold />} onClick={addIPAdapter}>
|
||||
{t('controlLayers.ipAdapter', { count: 1 })}
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem onClick={deleteAll} icon={<PiTrashSimpleBold />} color="error.300" isDisabled={!hasEntities}>
|
||||
{t('controlLayers.deleteAll', { count: 1 })}
|
||||
</MenuItem>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
CanvasEntityListMenuItems.displayName = 'CanvasEntityListMenu';
|
||||
@@ -1,77 +0,0 @@
|
||||
import { IconButton, Menu, MenuButton, MenuDivider, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
allEntitiesDeleted,
|
||||
controlLayerAdded,
|
||||
inpaintMaskAdded,
|
||||
ipaAdded,
|
||||
rasterLayerAdded,
|
||||
rgAdded,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntityCount } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiDotsThreeOutlineFill, PiPlusBold, PiTrashSimpleBold } from 'react-icons/pi';
|
||||
|
||||
export const CanvasEntityListMenu = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const hasEntities = useAppSelector((s) => {
|
||||
const count = selectEntityCount(s);
|
||||
return count > 0;
|
||||
});
|
||||
const addInpaintMask = useCallback(() => {
|
||||
dispatch(inpaintMaskAdded({ isSelected: true }));
|
||||
}, [dispatch]);
|
||||
const addRegionalGuidance = useCallback(() => {
|
||||
dispatch(rgAdded({ isSelected: true }));
|
||||
}, [dispatch]);
|
||||
const addRasterLayer = useCallback(() => {
|
||||
dispatch(rasterLayerAdded({ isSelected: true }));
|
||||
}, [dispatch]);
|
||||
const addControlLayer = useCallback(() => {
|
||||
dispatch(controlLayerAdded({ isSelected: true }));
|
||||
}, [dispatch]);
|
||||
const addIPAdapter = useCallback(() => {
|
||||
dispatch(ipaAdded({ isSelected: true }));
|
||||
}, [dispatch]);
|
||||
const deleteAll = useCallback(() => {
|
||||
dispatch(allEntitiesDeleted());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
aria-label={t('accessibility.menu')}
|
||||
icon={<PiDotsThreeOutlineFill />}
|
||||
variant="link"
|
||||
data-testid="control-layers-add-layer-menu-button"
|
||||
alignSelf="stretch"
|
||||
/>
|
||||
<MenuList>
|
||||
<MenuItem icon={<PiPlusBold />} onClick={addInpaintMask}>
|
||||
{t('controlLayers.inpaintMask', { count: 1 })}
|
||||
</MenuItem>
|
||||
<MenuItem icon={<PiPlusBold />} onClick={addRegionalGuidance}>
|
||||
{t('controlLayers.regionalGuidance', { count: 1 })}
|
||||
</MenuItem>
|
||||
<MenuItem icon={<PiPlusBold />} onClick={addRasterLayer}>
|
||||
{t('controlLayers.rasterLayer', { count: 1 })}
|
||||
</MenuItem>
|
||||
<MenuItem icon={<PiPlusBold />} onClick={addControlLayer}>
|
||||
{t('controlLayers.controlLayer', { count: 1 })}
|
||||
</MenuItem>
|
||||
<MenuItem icon={<PiPlusBold />} onClick={addIPAdapter}>
|
||||
{t('controlLayers.ipAdapter', { count: 1 })}
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem onClick={deleteAll} icon={<PiTrashSimpleBold />} color="error.300" isDisabled={!hasEntities}>
|
||||
{t('controlLayers.deleteAll', { count: 1 })}
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
);
|
||||
});
|
||||
|
||||
CanvasEntityListMenu.displayName = 'CanvasEntityListMenu';
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Button, ButtonGroup } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { sessionModeChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { sessionModeChanged } from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const CanvasModeSwitcher = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const mode = useAppSelector((s) => s.canvasV2.session.mode);
|
||||
const mode = useAppSelector((s) => s.canvasSession.mode);
|
||||
const onClickGenerate = useCallback(() => dispatch(sessionModeChanged({ mode: 'generate' })), [dispatch]);
|
||||
const onClickCompose = useCallback(() => dispatch(sessionModeChanged({ mode: 'compose' })), [dispatch]);
|
||||
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
import { Box, ContextMenu, MenuList } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasAddEntityButtons } from 'features/controlLayers/components/CanvasAddEntityButtons';
|
||||
import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList';
|
||||
import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityList';
|
||||
import { CanvasEntityListMenuItems } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityListMenuItems';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { selectEntityCount } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
export const CanvasPanelContent = memo(() => {
|
||||
const hasEntities = useAppSelector((s) => selectEntityCount(s) > 0);
|
||||
const renderMenu = useCallback(
|
||||
() => (
|
||||
<MenuList>
|
||||
<CanvasEntityListMenuItems />
|
||||
</MenuList>
|
||||
),
|
||||
[]
|
||||
);
|
||||
return (
|
||||
<CanvasManagerProviderGate>
|
||||
{!hasEntities && <CanvasAddEntityButtons />}
|
||||
{hasEntities && <CanvasEntityList />}
|
||||
<ContextMenu<HTMLDivElement> renderMenu={renderMenu}>
|
||||
{(ref) => (
|
||||
<Box ref={ref} w="full" h="full">
|
||||
{!hasEntities && <CanvasAddEntityButtons />}
|
||||
{hasEntities && <CanvasEntityList />}
|
||||
</Box>
|
||||
)}
|
||||
</ContextMenu>
|
||||
</CanvasManagerProviderGate>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -14,10 +14,9 @@ import {
|
||||
PopoverTrigger,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $canvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { MAX_CANVAS_SCALE, MIN_CANVAS_SCALE } from 'features/controlLayers/konva/constants';
|
||||
import { snapToNearest } from 'features/controlLayers/konva/util';
|
||||
import { $stageAttrs } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { clamp, round } from 'lodash-es';
|
||||
import { computed } from 'nanostores';
|
||||
import type { KeyboardEvent } from 'react';
|
||||
@@ -72,12 +71,10 @@ const sliderDefaultValue = mapScaleToSliderValue(100);
|
||||
|
||||
const snapCandidates = marks.slice(1, marks.length - 1);
|
||||
|
||||
const $scale = computed($stageAttrs, (attrs) => attrs.scale);
|
||||
|
||||
export const CanvasScale = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const canvasManager = useStore($canvasManager);
|
||||
const scale = useStore($scale);
|
||||
const canvasManager = useCanvasManager();
|
||||
const scale = useStore(computed(canvasManager.stateApi.$stageAttrs, (attrs) => attrs.scale));
|
||||
const [localScale, setLocalScale] = useState(scale * 100);
|
||||
|
||||
const onChangeSlider = useCallback(
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Badge } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectControlLayerEntityOrThrow } from 'features/controlLayers/store/controlLayersReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const ControlLayerBadges = memo(() => {
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
const { t } = useTranslation();
|
||||
const withTransparencyEffect = useAppSelector(
|
||||
(s) => selectControlLayerEntityOrThrow(s.canvasV2, id).withTransparencyEffect
|
||||
(s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).withTransparencyEffect
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -18,35 +18,35 @@ import type { ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/
|
||||
|
||||
export const ControlLayerControlAdapter = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
const controlAdapter = useControlLayerControlAdapter(entityIdentifier);
|
||||
|
||||
const onChangeBeginEndStepPct = useCallback(
|
||||
(beginEndStepPct: [number, number]) => {
|
||||
dispatch(controlLayerBeginEndStepPctChanged({ id: entityIdentifier.id, beginEndStepPct }));
|
||||
dispatch(controlLayerBeginEndStepPctChanged({ entityIdentifier, beginEndStepPct }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeControlMode = useCallback(
|
||||
(controlMode: ControlModeV2) => {
|
||||
dispatch(controlLayerControlModeChanged({ id: entityIdentifier.id, controlMode }));
|
||||
dispatch(controlLayerControlModeChanged({ entityIdentifier, controlMode }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeWeight = useCallback(
|
||||
(weight: number) => {
|
||||
dispatch(controlLayerWeightChanged({ id: entityIdentifier.id, weight }));
|
||||
dispatch(controlLayerWeightChanged({ entityIdentifier, weight }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeModel = useCallback(
|
||||
(modelConfig: ControlNetModelConfig | T2IAdapterModelConfig) => {
|
||||
dispatch(controlLayerModelChanged({ id: entityIdentifier.id, modelConfig }));
|
||||
dispatch(controlLayerModelChanged({ entityIdentifier, modelConfig }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -18,7 +18,7 @@ export const ControlLayerControlAdapterModel = memo(({ modelKey, onChange: onCha
|
||||
const { t } = useTranslation();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const canvasManager = useCanvasManager();
|
||||
const currentBaseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
||||
const currentBaseModel = useAppSelector((s) => s.params.model?.base);
|
||||
const [modelConfigs, { isLoading }] = useControlNetAndT2IAdapterModels();
|
||||
const selectedModel = useMemo(() => modelConfigs.find((m) => m.key === modelKey), [modelConfigs, modelKey]);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { ControlLayer } from 'features/controlLayers/components/ControlLayer/ControlLayer';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
@@ -11,7 +11,9 @@ const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) =
|
||||
});
|
||||
|
||||
export const ControlLayerEntityList = memo(() => {
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'control_layer'));
|
||||
const isSelected = useAppSelector((s) =>
|
||||
Boolean(s.canvasV2.present.selectedEntityIdentifier?.type === 'control_layer')
|
||||
);
|
||||
const layerIds = useAppSelector(selectEntityIds);
|
||||
|
||||
if (layerIds.length === 0) {
|
||||
|
||||
@@ -9,11 +9,11 @@ import { PiLightningBold } from 'react-icons/pi';
|
||||
export const ControlLayerMenuItemsControlToRaster = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
|
||||
const convertControlLayerToRasterLayer = useCallback(() => {
|
||||
dispatch(controlLayerConvertedToRasterLayer({ id: entityIdentifier.id }));
|
||||
}, [dispatch, entityIdentifier.id]);
|
||||
dispatch(controlLayerConvertedToRasterLayer({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={convertControlLayerToRasterLayer} icon={<PiLightningBold />}>
|
||||
|
||||
@@ -2,11 +2,8 @@ import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import {
|
||||
controlLayerWithTransparencyEffectToggled,
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectControlLayerEntityOrThrow } from 'features/controlLayers/store/controlLayersReducers';
|
||||
import { controlLayerWithTransparencyEffectToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiDropHalfBold } from 'react-icons/pi';
|
||||
@@ -14,18 +11,18 @@ import { PiDropHalfBold } from 'react-icons/pi';
|
||||
export const ControlLayerMenuItemsTransparencyEffect = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('control_layer');
|
||||
const selectWithTransparencyEffect = useMemo(
|
||||
() =>
|
||||
createSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const entity = selectControlLayerEntityOrThrow(canvasV2, entityIdentifier.id);
|
||||
const entity = selectEntityOrThrow(canvasV2, entityIdentifier);
|
||||
return entity.withTransparencyEffect;
|
||||
}),
|
||||
[entityIdentifier.id]
|
||||
[entityIdentifier]
|
||||
);
|
||||
const withTransparencyEffect = useAppSelector(selectWithTransparencyEffect);
|
||||
const onToggle = useCallback(() => {
|
||||
dispatch(controlLayerWithTransparencyEffectToggled({ id: entityIdentifier.id }));
|
||||
dispatch(controlLayerWithTransparencyEffectToggled({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -33,9 +33,11 @@ export const CanvasEditor = memo(() => {
|
||||
<ControlLayersToolbar />
|
||||
<StageComponent />
|
||||
<Flex position="absolute" bottom={16} gap={2} align="center" justify="center">
|
||||
<StagingAreaIsStagingGate>
|
||||
<StagingAreaToolbar />
|
||||
</StagingAreaIsStagingGate>
|
||||
<CanvasManagerProviderGate>
|
||||
<StagingAreaIsStagingGate>
|
||||
<StagingAreaToolbar />
|
||||
</StagingAreaIsStagingGate>
|
||||
</CanvasManagerProviderGate>
|
||||
</Flex>
|
||||
<Flex position="absolute" bottom={16}>
|
||||
<CanvasManagerProviderGate>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
import { Flex, Spacer } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasModeSwitcher } from 'features/controlLayers/components/CanvasModeSwitcher';
|
||||
import { CanvasResetViewButton } from 'features/controlLayers/components/CanvasResetViewButton';
|
||||
import { CanvasScale } from 'features/controlLayers/components/CanvasScale';
|
||||
import { CanvasSettingsPopover } from 'features/controlLayers/components/Settings/CanvasSettingsPopover';
|
||||
import { ToolBrushWidth } from 'features/controlLayers/components/Tool/ToolBrushWidth';
|
||||
import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser';
|
||||
import { ToolEraserWidth } from 'features/controlLayers/components/Tool/ToolEraserWidth';
|
||||
import { ToolFillColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
|
||||
import { ToolSettings } from 'features/controlLayers/components/Tool/ToolSettings';
|
||||
import { UndoRedoButtonGroup } from 'features/controlLayers/components/UndoRedoButtonGroup';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
|
||||
@@ -16,15 +14,13 @@ import { ViewerToggleMenu } from 'features/gallery/components/ImageViewer/Viewer
|
||||
import { memo } from 'react';
|
||||
|
||||
export const ControlLayersToolbar = memo(() => {
|
||||
const tool = useAppSelector((s) => s.canvasV2.tool.selected);
|
||||
return (
|
||||
<CanvasManagerProviderGate>
|
||||
<Flex w="full" gap={2} alignItems="center">
|
||||
<ToggleProgressButton />
|
||||
<ToolChooser />
|
||||
<Spacer />
|
||||
{tool === 'brush' && <ToolBrushWidth />}
|
||||
{tool === 'eraser' && <ToolEraserWidth />}
|
||||
<ToolSettings />
|
||||
<Spacer />
|
||||
<CanvasScale />
|
||||
<CanvasResetViewButton />
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
import { Box, Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
$isDrawing,
|
||||
$isMouseDown,
|
||||
$lastAddedPoint,
|
||||
$lastCursorPos,
|
||||
$lastMouseDownPos,
|
||||
$stageAttrs,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { round } from 'lodash-es';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const HeadsUpDisplay = memo(() => {
|
||||
const stageAttrs = useStore($stageAttrs);
|
||||
const cursorPos = useStore($lastCursorPos);
|
||||
const isDrawing = useStore($isDrawing);
|
||||
const isMouseDown = useStore($isMouseDown);
|
||||
const lastMouseDownPos = useStore($lastMouseDownPos);
|
||||
const lastAddedPoint = useStore($lastAddedPoint);
|
||||
const bbox = useAppSelector((s) => s.canvasV2.bbox);
|
||||
const canvasManager = useCanvasManager();
|
||||
const stageAttrs = useStore(canvasManager.stateApi.$stageAttrs);
|
||||
const cursorPos = useStore(canvasManager.stateApi.$lastCursorPos);
|
||||
const isDrawing = useStore(canvasManager.stateApi.$isDrawing);
|
||||
const isMouseDown = useStore(canvasManager.stateApi.$isMouseDown);
|
||||
const lastMouseDownPos = useStore(canvasManager.stateApi.$lastMouseDownPos);
|
||||
const lastAddedPoint = useStore(canvasManager.stateApi.$lastAddedPoint);
|
||||
const bbox = useAppSelector((s) => s.canvasV2.present.bbox);
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" bg="blackAlpha.400" borderBottomEndRadius="base" p={2} minW={64} gap={2}>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { IPAdapter } from 'features/controlLayers/components/IPAdapter/IPAdapter';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
@@ -12,7 +12,7 @@ const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) =
|
||||
});
|
||||
|
||||
export const IPAdapterList = memo(() => {
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'ip_adapter'));
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.present.selectedEntityIdentifier?.type === 'ip_adapter'));
|
||||
const ipaIds = useAppSelector(selectEntityIds);
|
||||
|
||||
if (ipaIds.length === 0) {
|
||||
|
||||
@@ -24,7 +24,7 @@ type Props = {
|
||||
|
||||
export const IPAdapterModel = memo(({ modelKey, onChangeModel, clipVisionModel, onChangeCLIPVisionModel }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const currentBaseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
||||
const currentBaseModel = useAppSelector((s) => s.params.model?.base);
|
||||
const [modelConfigs, { isLoading }] = useIPAdapterModels();
|
||||
const selectedModel = useMemo(() => modelConfigs.find((m) => m.key === modelKey), [modelConfigs, modelKey]);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
ipaModelChanged,
|
||||
ipaWeightChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectIPAdapterEntityOrThrow } from 'features/controlLayers/store/ipAdaptersReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||
import type { IPAImageDropData } from 'features/dnd/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@@ -24,53 +24,59 @@ import { IPAdapterModel } from './IPAdapterModel';
|
||||
|
||||
export const IPAdapterSettings = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const ipAdapter = useAppSelector((s) => selectIPAdapterEntityOrThrow(s.canvasV2, id).ipAdapter);
|
||||
const entityIdentifier = useEntityIdentifierContext('ip_adapter');
|
||||
const ipAdapter = useAppSelector((s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).ipAdapter);
|
||||
|
||||
const onChangeBeginEndStepPct = useCallback(
|
||||
(beginEndStepPct: [number, number]) => {
|
||||
dispatch(ipaBeginEndStepPctChanged({ id, beginEndStepPct }));
|
||||
dispatch(ipaBeginEndStepPctChanged({ entityIdentifier, beginEndStepPct }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeWeight = useCallback(
|
||||
(weight: number) => {
|
||||
dispatch(ipaWeightChanged({ id, weight }));
|
||||
dispatch(ipaWeightChanged({ entityIdentifier, weight }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeIPMethod = useCallback(
|
||||
(method: IPMethodV2) => {
|
||||
dispatch(ipaMethodChanged({ id, method }));
|
||||
dispatch(ipaMethodChanged({ entityIdentifier, method }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeModel = useCallback(
|
||||
(modelConfig: IPAdapterModelConfig) => {
|
||||
dispatch(ipaModelChanged({ id, modelConfig }));
|
||||
dispatch(ipaModelChanged({ entityIdentifier, modelConfig }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeCLIPVisionModel = useCallback(
|
||||
(clipVisionModel: CLIPVisionModelV2) => {
|
||||
dispatch(ipaCLIPVisionModelChanged({ id, clipVisionModel }));
|
||||
dispatch(ipaCLIPVisionModelChanged({ entityIdentifier, clipVisionModel }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const onChangeImage = useCallback(
|
||||
(imageDTO: ImageDTO | null) => {
|
||||
dispatch(ipaImageChanged({ id, imageDTO }));
|
||||
dispatch(ipaImageChanged({ entityIdentifier, imageDTO }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
|
||||
const droppableData = useMemo<IPAImageDropData>(() => ({ actionType: 'SET_IPA_IMAGE', context: { id }, id }), [id]);
|
||||
const postUploadAction = useMemo<IPALayerImagePostUploadAction>(() => ({ type: 'SET_IPA_IMAGE', id }), [id]);
|
||||
const droppableData = useMemo<IPAImageDropData>(
|
||||
() => ({ actionType: 'SET_IPA_IMAGE', context: { id: entityIdentifier.id }, id: entityIdentifier.id }),
|
||||
[entityIdentifier.id]
|
||||
);
|
||||
const postUploadAction = useMemo<IPALayerImagePostUploadAction>(
|
||||
() => ({ type: 'SET_IPA_IMAGE', id: entityIdentifier.id }),
|
||||
[entityIdentifier.id]
|
||||
);
|
||||
|
||||
return (
|
||||
<CanvasEntitySettingsWrapper>
|
||||
@@ -95,7 +101,7 @@ export const IPAdapterSettings = memo(() => {
|
||||
<IPAdapterImagePreview
|
||||
image={ipAdapter.image ?? null}
|
||||
onChangeImage={onChangeImage}
|
||||
ipAdapterId={id}
|
||||
ipAdapterId={entityIdentifier.id}
|
||||
droppableData={droppableData}
|
||||
postUploadAction={postUploadAction}
|
||||
/>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { InpaintMask } from 'features/controlLayers/components/InpaintMask/InpaintMask';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
@@ -11,7 +11,9 @@ const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) =
|
||||
});
|
||||
|
||||
export const InpaintMaskList = memo(() => {
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'inpaint_mask'));
|
||||
const isSelected = useAppSelector((s) =>
|
||||
Boolean(s.canvasV2.present.selectedEntityIdentifier?.type === 'inpaint_mask')
|
||||
);
|
||||
const entityIds = useAppSelector(selectEntityIds);
|
||||
|
||||
if (entityIds.length === 0) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { inpaintMaskFillColorChanged, inpaintMaskFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectInpaintMaskEntityOrThrow } from 'features/controlLayers/store/inpaintMaskReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type { FillStyle, RgbColor } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -13,8 +13,8 @@ import { useTranslation } from 'react-i18next';
|
||||
export const InpaintMaskMaskFillColorPicker = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const fill = useAppSelector((s) => selectInpaintMaskEntityOrThrow(s.canvasV2, entityIdentifier.id).fill);
|
||||
const entityIdentifier = useEntityIdentifierContext('inpaint_mask');
|
||||
const fill = useAppSelector((s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).fill);
|
||||
|
||||
const onChangeFillColor = useCallback(
|
||||
(color: RgbColor) => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { RasterLayer } from 'features/controlLayers/components/RasterLayer/RasterLayer';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
@@ -11,7 +11,9 @@ const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) =
|
||||
});
|
||||
|
||||
export const RasterLayerEntityList = memo(() => {
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'raster_layer'));
|
||||
const isSelected = useAppSelector((s) =>
|
||||
Boolean(s.canvasV2.present.selectedEntityIdentifier?.type === 'raster_layer')
|
||||
);
|
||||
const layerIds = useAppSelector(selectEntityIds);
|
||||
|
||||
if (layerIds.length === 0) {
|
||||
|
||||
@@ -9,11 +9,11 @@ import { PiLightningBold } from 'react-icons/pi';
|
||||
export const RasterLayerMenuItemsRasterToControl = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('raster_layer');
|
||||
|
||||
const convertRasterLayerToControlLayer = useCallback(() => {
|
||||
dispatch(rasterLayerConvertedToControlLayer({ id: entityIdentifier.id }));
|
||||
}, [dispatch, entityIdentifier.id]);
|
||||
dispatch(rasterLayerConvertedToControlLayer({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem onClick={convertRasterLayerToControlLayer} icon={<PiLightningBold />}>
|
||||
|
||||
@@ -1,44 +1,42 @@
|
||||
import { Button, Flex } from '@invoke-ai/ui-library';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import {
|
||||
rgIPAdapterAdded,
|
||||
rgNegativePromptChanged,
|
||||
rgPositivePromptChanged,
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiPlusBold } from 'react-icons/pi';
|
||||
|
||||
type AddPromptButtonProps = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const RegionalGuidanceAddPromptsIPAdapterButtons = ({ id }: AddPromptButtonProps) => {
|
||||
export const RegionalGuidanceAddPromptsIPAdapterButtons = () => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const selectValidActions = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const rg = canvasV2.regions.entities.find((rg) => rg.id === id);
|
||||
const entity = selectEntityOrThrow(canvasV2, entityIdentifier);
|
||||
return {
|
||||
canAddPositivePrompt: rg?.positivePrompt === null,
|
||||
canAddNegativePrompt: rg?.negativePrompt === null,
|
||||
canAddPositivePrompt: entity?.positivePrompt === null,
|
||||
canAddNegativePrompt: entity?.negativePrompt === null,
|
||||
};
|
||||
}),
|
||||
[id]
|
||||
[entityIdentifier]
|
||||
);
|
||||
const validActions = useAppSelector(selectValidActions);
|
||||
const addPositivePrompt = useCallback(() => {
|
||||
dispatch(rgPositivePromptChanged({ id, prompt: '' }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgPositivePromptChanged({ entityIdentifier, prompt: '' }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const addNegativePrompt = useCallback(() => {
|
||||
dispatch(rgNegativePromptChanged({ id, prompt: '' }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgNegativePromptChanged({ entityIdentifier, prompt: '' }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const addIPAdapter = useCallback(() => {
|
||||
dispatch(rgIPAdapterAdded({ id }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgIPAdapterAdded({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<Flex w="full" p={2} justifyContent="space-between">
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Badge } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RegionalGuidanceBadges = memo(() => {
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const autoNegative = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).autoNegative);
|
||||
const autoNegative = useAppSelector((s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).autoNegative);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntityGroupList } from 'features/controlLayers/components/common/CanvasEntityGroupList';
|
||||
import { RegionalGuidance } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidance';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
@@ -11,7 +11,9 @@ const selectEntityIds = createMemoizedSelector(selectCanvasV2Slice, (canvasV2) =
|
||||
});
|
||||
|
||||
export const RegionalGuidanceEntityList = memo(() => {
|
||||
const isSelected = useAppSelector((s) => Boolean(s.canvasV2.selectedEntityIdentifier?.type === 'regional_guidance'));
|
||||
const isSelected = useAppSelector((s) =>
|
||||
Boolean(s.canvasV2.present.selectedEntityIdentifier?.type === 'regional_guidance')
|
||||
);
|
||||
const rgIds = useAppSelector(selectEntityIds);
|
||||
|
||||
if (rgIds.length === 0) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Weight } from 'features/controlLayers/components/common/Weight';
|
||||
import { IPAdapterImagePreview } from 'features/controlLayers/components/IPAdapter/IPAdapterImagePreview';
|
||||
import { IPAdapterMethod } from 'features/controlLayers/components/IPAdapter/IPAdapterMethod';
|
||||
import { IPAdapterModel } from 'features/controlLayers/components/IPAdapter/IPAdapterModel';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import {
|
||||
rgIPAdapterBeginEndStepPctChanged,
|
||||
rgIPAdapterCLIPVisionModelChanged,
|
||||
@@ -14,7 +15,7 @@ import {
|
||||
rgIPAdapterModelChanged,
|
||||
rgIPAdapterWeightChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectRegionalGuidanceIPAdapter } from 'features/controlLayers/store/selectors';
|
||||
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/store/types';
|
||||
import type { RGIPAdapterImageDropData } from 'features/dnd/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
@@ -23,71 +24,75 @@ import type { ImageDTO, IPAdapterModelConfig, RGIPAdapterImagePostUploadAction }
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
ipAdapterId: string;
|
||||
ipAdapterNumber: number;
|
||||
};
|
||||
|
||||
export const RegionalGuidanceIPAdapterSettings = memo(({ id, ipAdapterId, ipAdapterNumber }: Props) => {
|
||||
export const RegionalGuidanceIPAdapterSettings = memo(({ ipAdapterId, ipAdapterNumber }: Props) => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const dispatch = useAppDispatch();
|
||||
const onDeleteIPAdapter = useCallback(() => {
|
||||
dispatch(rgIPAdapterDeleted({ id, ipAdapterId }));
|
||||
}, [dispatch, ipAdapterId, id]);
|
||||
dispatch(rgIPAdapterDeleted({ entityIdentifier, ipAdapterId }));
|
||||
}, [dispatch, entityIdentifier, ipAdapterId]);
|
||||
const ipAdapter = useAppSelector((s) => {
|
||||
const ipa = selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).ipAdapters.find((ipa) => ipa.id === ipAdapterId);
|
||||
const ipa = selectRegionalGuidanceIPAdapter(s.canvasV2.present, entityIdentifier, ipAdapterId);
|
||||
assert(ipa, `Regional GuidanceIP Adapter with id ${ipAdapterId} not found`);
|
||||
return ipa;
|
||||
});
|
||||
|
||||
const onChangeBeginEndStepPct = useCallback(
|
||||
(beginEndStepPct: [number, number]) => {
|
||||
dispatch(rgIPAdapterBeginEndStepPctChanged({ id, ipAdapterId, beginEndStepPct }));
|
||||
dispatch(rgIPAdapterBeginEndStepPctChanged({ entityIdentifier, ipAdapterId, beginEndStepPct }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const onChangeWeight = useCallback(
|
||||
(weight: number) => {
|
||||
dispatch(rgIPAdapterWeightChanged({ id, ipAdapterId, weight }));
|
||||
dispatch(rgIPAdapterWeightChanged({ entityIdentifier, ipAdapterId, weight }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const onChangeIPMethod = useCallback(
|
||||
(method: IPMethodV2) => {
|
||||
dispatch(rgIPAdapterMethodChanged({ id, ipAdapterId, method }));
|
||||
dispatch(rgIPAdapterMethodChanged({ entityIdentifier, ipAdapterId, method }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const onChangeModel = useCallback(
|
||||
(modelConfig: IPAdapterModelConfig) => {
|
||||
dispatch(rgIPAdapterModelChanged({ id, ipAdapterId, modelConfig }));
|
||||
dispatch(rgIPAdapterModelChanged({ entityIdentifier, ipAdapterId, modelConfig }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const onChangeCLIPVisionModel = useCallback(
|
||||
(clipVisionModel: CLIPVisionModelV2) => {
|
||||
dispatch(rgIPAdapterCLIPVisionModelChanged({ id, ipAdapterId, clipVisionModel }));
|
||||
dispatch(rgIPAdapterCLIPVisionModelChanged({ entityIdentifier, ipAdapterId, clipVisionModel }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const onChangeImage = useCallback(
|
||||
(imageDTO: ImageDTO | null) => {
|
||||
dispatch(rgIPAdapterImageChanged({ id, ipAdapterId, imageDTO }));
|
||||
dispatch(rgIPAdapterImageChanged({ entityIdentifier, ipAdapterId, imageDTO }));
|
||||
},
|
||||
[dispatch, ipAdapterId, id]
|
||||
[dispatch, entityIdentifier, ipAdapterId]
|
||||
);
|
||||
|
||||
const droppableData = useMemo<RGIPAdapterImageDropData>(
|
||||
() => ({ actionType: 'SET_RG_IP_ADAPTER_IMAGE', context: { id, ipAdapterId }, id }),
|
||||
[ipAdapterId, id]
|
||||
() => ({
|
||||
actionType: 'SET_RG_IP_ADAPTER_IMAGE',
|
||||
context: { id: entityIdentifier.id, ipAdapterId },
|
||||
id: entityIdentifier.id,
|
||||
}),
|
||||
[entityIdentifier.id, ipAdapterId]
|
||||
);
|
||||
const postUploadAction = useMemo<RGIPAdapterImagePostUploadAction>(
|
||||
() => ({ type: 'SET_RG_IP_ADAPTER_IMAGE', id, ipAdapterId }),
|
||||
[ipAdapterId, id]
|
||||
() => ({ type: 'SET_RG_IP_ADAPTER_IMAGE', id: entityIdentifier.id, ipAdapterId }),
|
||||
[entityIdentifier.id, ipAdapterId]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,25 +3,23 @@ import { EMPTY_ARRAY } from 'app/store/constants';
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { RegionalGuidanceIPAdapterSettings } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceIPAdapterSettings';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { Fragment, memo, useMemo } from 'react';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
};
|
||||
export const RegionalGuidanceIPAdapters = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
|
||||
export const RegionalGuidanceIPAdapters = memo(({ id }: Props) => {
|
||||
const selectIPAdapterIds = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const ipAdapterIds = selectRegionalGuidanceEntityOrThrow(canvasV2, id).ipAdapters.map(({ id }) => id);
|
||||
const ipAdapterIds = selectEntityOrThrow(canvasV2, entityIdentifier).ipAdapters.map(({ id }) => id);
|
||||
if (ipAdapterIds.length === 0) {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
return ipAdapterIds;
|
||||
}),
|
||||
[id]
|
||||
[entityIdentifier]
|
||||
);
|
||||
|
||||
const ipAdapterIds = useAppSelector(selectIPAdapterIds);
|
||||
@@ -35,7 +33,7 @@ export const RegionalGuidanceIPAdapters = memo(({ id }: Props) => {
|
||||
{ipAdapterIds.map((ipAdapterId, index) => (
|
||||
<Fragment key={ipAdapterId}>
|
||||
{index > 0 && <Divider />}
|
||||
<RegionalGuidanceIPAdapterSettings id={id} ipAdapterId={ipAdapterId} ipAdapterNumber={index + 1} />
|
||||
<RegionalGuidanceIPAdapterSettings ipAdapterId={ipAdapterId} ipAdapterNumber={index + 1} />
|
||||
</Fragment>
|
||||
))}
|
||||
</>
|
||||
|
||||
@@ -5,27 +5,27 @@ import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgFillColorChanged, rgFillStyleChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type { FillStyle, RgbColor } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RegionalGuidanceMaskFillColorPicker = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const fill = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, entityIdentifier.id).fill);
|
||||
const fill = useAppSelector((s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).fill);
|
||||
const onChangeFillColor = useCallback(
|
||||
(color: RgbColor) => {
|
||||
dispatch(rgFillColorChanged({ id: entityIdentifier.id, color }));
|
||||
dispatch(rgFillColorChanged({ entityIdentifier, color }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
const onChangeFillStyle = useCallback(
|
||||
(style: FillStyle) => {
|
||||
dispatch(rgFillStyleChanged({ id: entityIdentifier.id, style }));
|
||||
dispatch(rgFillStyleChanged({ entityIdentifier, style }));
|
||||
},
|
||||
[dispatch, entityIdentifier.id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
return (
|
||||
<Popover isLazy>
|
||||
|
||||
@@ -6,36 +6,36 @@ import {
|
||||
rgIPAdapterAdded,
|
||||
rgNegativePromptChanged,
|
||||
rgPositivePromptChanged,
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const RegionalGuidanceMenuItemsAddPromptsAndIPAdapter = memo(() => {
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const selectValidActions = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const rg = canvasV2.regions.entities.find((rg) => rg.id === id);
|
||||
const entity = selectEntity(canvasV2, entityIdentifier);
|
||||
return {
|
||||
canAddPositivePrompt: rg?.positivePrompt === null,
|
||||
canAddNegativePrompt: rg?.negativePrompt === null,
|
||||
canAddPositivePrompt: entity?.positivePrompt === null,
|
||||
canAddNegativePrompt: entity?.negativePrompt === null,
|
||||
};
|
||||
}),
|
||||
[id]
|
||||
[entityIdentifier]
|
||||
);
|
||||
const validActions = useAppSelector(selectValidActions);
|
||||
const addPositivePrompt = useCallback(() => {
|
||||
dispatch(rgPositivePromptChanged({ id: id, prompt: '' }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgPositivePromptChanged({ entityIdentifier, prompt: '' }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const addNegativePrompt = useCallback(() => {
|
||||
dispatch(rgNegativePromptChanged({ id: id, prompt: '' }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgNegativePromptChanged({ entityIdentifier, prompt: '' }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const addIPAdapter = useCallback(() => {
|
||||
dispatch(rgIPAdapterAdded({ id }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgIPAdapterAdded({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -2,19 +2,19 @@ import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgAutoNegativeToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiSelectionInverseBold } from 'react-icons/pi';
|
||||
|
||||
export const RegionalGuidanceMenuItemsAutoNegative = memo(() => {
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const autoNegative = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).autoNegative);
|
||||
const autoNegative = useAppSelector((s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).autoNegative);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(rgAutoNegativeToggled({ id }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgAutoNegativeToggled({ entityIdentifier }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
|
||||
return (
|
||||
<MenuItem icon={<PiSelectionInverseBold />} onClick={onClick}>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { RegionalGuidanceDeletePromptButton } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceDeletePromptButton';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgNegativePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||
@@ -10,24 +11,21 @@ import { usePrompt } from 'features/prompt/usePrompt';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const RegionalGuidanceNegativePrompt = memo(({ id }: Props) => {
|
||||
const prompt = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).negativePrompt ?? '');
|
||||
export const RegionalGuidanceNegativePrompt = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const prompt = useAppSelector((s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).negativePrompt ?? '');
|
||||
const dispatch = useAppDispatch();
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const { t } = useTranslation();
|
||||
const _onChange = useCallback(
|
||||
(v: string) => {
|
||||
dispatch(rgNegativePromptChanged({ id, prompt: v }));
|
||||
dispatch(rgNegativePromptChanged({ entityIdentifier, prompt: v }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
const onDeletePrompt = useCallback(() => {
|
||||
dispatch(rgNegativePromptChanged({ id, prompt: null }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgNegativePromptChanged({ entityIdentifier, prompt: null }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const { onChange, isOpen, onClose, onOpen, onSelect, onKeyDown } = usePrompt({
|
||||
prompt,
|
||||
textareaRef,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { RegionalGuidanceDeletePromptButton } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceDeletePromptButton';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { rgPositivePromptChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||
@@ -10,24 +11,21 @@ import { usePrompt } from 'features/prompt/usePrompt';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const RegionalGuidancePositivePrompt = memo(({ id }: Props) => {
|
||||
const prompt = useAppSelector((s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).positivePrompt ?? '');
|
||||
export const RegionalGuidancePositivePrompt = memo(() => {
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const prompt = useAppSelector((s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).positivePrompt ?? '');
|
||||
const dispatch = useAppDispatch();
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const { t } = useTranslation();
|
||||
const _onChange = useCallback(
|
||||
(v: string) => {
|
||||
dispatch(rgPositivePromptChanged({ id, prompt: v }));
|
||||
dispatch(rgPositivePromptChanged({ entityIdentifier, prompt: v }));
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, entityIdentifier]
|
||||
);
|
||||
const onDeletePrompt = useCallback(() => {
|
||||
dispatch(rgPositivePromptChanged({ id, prompt: null }));
|
||||
}, [dispatch, id]);
|
||||
dispatch(rgPositivePromptChanged({ entityIdentifier, prompt: null }));
|
||||
}, [dispatch, entityIdentifier]);
|
||||
const { onChange, isOpen, onClose, onOpen, onSelect, onKeyDown } = usePrompt({
|
||||
prompt,
|
||||
textareaRef,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
|
||||
import { RegionalGuidanceAddPromptsIPAdapterButtons } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceAddPromptsIPAdapterButtons';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { selectRegionalGuidanceEntityOrThrow } from 'features/controlLayers/store/regionsReducers';
|
||||
import { selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { RegionalGuidanceIPAdapters } from './RegionalGuidanceIPAdapters';
|
||||
@@ -11,35 +11,33 @@ import { RegionalGuidanceNegativePrompt } from './RegionalGuidanceNegativePrompt
|
||||
import { RegionalGuidancePositivePrompt } from './RegionalGuidancePositivePrompt';
|
||||
|
||||
export const RegionalGuidanceSettings = memo(() => {
|
||||
const { id } = useEntityIdentifierContext();
|
||||
const entityIdentifier = useEntityIdentifierContext('regional_guidance');
|
||||
const hasPositivePrompt = useAppSelector(
|
||||
(s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).positivePrompt !== null
|
||||
(s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).positivePrompt !== null
|
||||
);
|
||||
const hasNegativePrompt = useAppSelector(
|
||||
(s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).negativePrompt !== null
|
||||
(s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).negativePrompt !== null
|
||||
);
|
||||
const hasIPAdapters = useAppSelector(
|
||||
(s) => selectRegionalGuidanceEntityOrThrow(s.canvasV2, id).ipAdapters.length > 0
|
||||
(s) => selectEntityOrThrow(s.canvasV2.present, entityIdentifier).ipAdapters.length > 0
|
||||
);
|
||||
|
||||
return (
|
||||
<CanvasEntitySettingsWrapper>
|
||||
{!hasPositivePrompt && !hasNegativePrompt && !hasIPAdapters && (
|
||||
<RegionalGuidanceAddPromptsIPAdapterButtons id={id} />
|
||||
)}
|
||||
{!hasPositivePrompt && !hasNegativePrompt && !hasIPAdapters && <RegionalGuidanceAddPromptsIPAdapterButtons />}
|
||||
{hasPositivePrompt && (
|
||||
<>
|
||||
<RegionalGuidancePositivePrompt id={id} />
|
||||
<RegionalGuidancePositivePrompt />
|
||||
{(hasNegativePrompt || hasIPAdapters) && <Divider />}
|
||||
</>
|
||||
)}
|
||||
{hasNegativePrompt && (
|
||||
<>
|
||||
<RegionalGuidanceNegativePrompt id={id} />
|
||||
<RegionalGuidanceNegativePrompt />
|
||||
{hasIPAdapters && <Divider />}
|
||||
</>
|
||||
)}
|
||||
{hasIPAdapters && <RegionalGuidanceIPAdapters id={id} />}
|
||||
{hasIPAdapters && <RegionalGuidanceIPAdapters />}
|
||||
</CanvasEntitySettingsWrapper>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { settingsAutoSaveToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { settingsAutoSaveToggled } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const CanvasSettingsAutoSaveCheckbox = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const autoSave = useAppSelector((s) => s.canvasV2.settings.autoSave);
|
||||
const autoSave = useAppSelector((s) => s.canvasSettings.autoSave);
|
||||
const onChange = useCallback(() => dispatch(settingsAutoSaveToggled()), [dispatch]);
|
||||
return (
|
||||
<FormControl w="full">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { clipToBboxChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { clipToBboxChanged } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
|
||||
export const CanvasSettingsClipToBboxCheckbox = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const clipToBbox = useAppSelector((s) => s.canvasV2.settings.clipToBbox);
|
||||
const clipToBbox = useAppSelector((s) => s.canvasSettings.clipToBbox);
|
||||
const onChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => dispatch(clipToBboxChanged(e.target.checked)),
|
||||
[dispatch]
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { settingsDynamicGridToggled } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { settingsDynamicGridToggled } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const CanvasSettingsDynamicGridSwitch = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const dynamicGrid = useAppSelector((s) => s.canvasV2.settings.dynamicGrid);
|
||||
const dynamicGrid = useAppSelector((s) => s.canvasSettings.dynamicGrid);
|
||||
const onChange = useCallback(() => {
|
||||
dispatch(settingsDynamicGridToggled());
|
||||
}, [dispatch]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { invertScrollChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { invertScrollChanged } from 'features/controlLayers/store/toolSlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
|
||||
export const CanvasSettingsInvertScrollCheckbox = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const invertScroll = useAppSelector((s) => s.canvasV2.tool.invertScroll);
|
||||
const invertScroll = useAppSelector((s) => s.tool.invertScroll);
|
||||
const onChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => dispatch(invertScrollChanged(e.target.checked)),
|
||||
[dispatch]
|
||||
|
||||
@@ -38,8 +38,8 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
||||
}
|
||||
|
||||
const manager = new CanvasManager(stage, container, store, socket);
|
||||
const cleanup = manager.initialize();
|
||||
return cleanup;
|
||||
manager.initialize();
|
||||
return manager.destroy;
|
||||
}, [asPreview, container, socket, stage, store]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
@@ -52,7 +52,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export const StageComponent = memo(({ asPreview = false }: Props) => {
|
||||
const dynamicGrid = useAppSelector((s) => s.canvasV2.settings.dynamicGrid);
|
||||
const dynamicGrid = useAppSelector((s) => s.canvasSettings.dynamicGrid);
|
||||
|
||||
const [stage] = useState(
|
||||
() =>
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { PropsWithChildren } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const StagingAreaIsStagingGate = memo((props: PropsWithChildren) => {
|
||||
const isStaging = useAppSelector((s) => s.canvasV2.session.isStaging);
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
|
||||
if (!isStaging) {
|
||||
return null;
|
||||
|
||||
@@ -2,14 +2,14 @@ import { Button, ButtonGroup, IconButton } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { INTERACTION_SCOPES, useScopeOnMount } from 'common/hooks/interactionScopes';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import {
|
||||
$shouldShowStagedImage,
|
||||
sessionNextStagedImageSelected,
|
||||
sessionPrevStagedImageSelected,
|
||||
sessionStagedImageDiscarded,
|
||||
sessionStagingAreaImageAccepted,
|
||||
sessionStagingAreaReset,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
} from 'features/controlLayers/store/canvasSessionSlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -27,8 +27,9 @@ import { useChangeImageIsIntermediateMutation } from 'services/api/endpoints/ima
|
||||
|
||||
export const StagingAreaToolbar = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const session = useAppSelector((s) => s.canvasV2.session);
|
||||
const shouldShowStagedImage = useStore($shouldShowStagedImage);
|
||||
const session = useAppSelector((s) => s.canvasSession);
|
||||
const canvasManager = useCanvasManager();
|
||||
const shouldShowStagedImage = useStore(canvasManager.stateApi.$shouldShowStagedImage);
|
||||
const images = useMemo(() => session.stagedImages, [session]);
|
||||
const selectedImage = useMemo(() => {
|
||||
return images[session.selectedStagedImageIndex] ?? null;
|
||||
@@ -70,8 +71,8 @@ export const StagingAreaToolbar = memo(() => {
|
||||
}, [dispatch]);
|
||||
|
||||
const onToggleShouldShowStagedImage = useCallback(() => {
|
||||
$shouldShowStagedImage.set(!shouldShowStagedImage);
|
||||
}, [shouldShowStagedImage]);
|
||||
canvasManager.stateApi.$shouldShowStagedImage.set(!shouldShowStagedImage);
|
||||
}, [canvasManager.stateApi.$shouldShowStagedImage, shouldShowStagedImage]);
|
||||
|
||||
const onSaveStagingImage = useCallback(() => {
|
||||
if (!selectedImage) {
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { toolChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiBoundingBoxBold } from 'react-icons/pi';
|
||||
|
||||
export const ToolBboxButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const selectBbox = useSelectTool('bbox');
|
||||
const isSelected = useToolIsSelected('bbox');
|
||||
const isFiltering = useIsFiltering();
|
||||
const isTransforming = useIsTransforming();
|
||||
const isStaging = useAppSelector((s) => s.canvasV2.session.isStaging);
|
||||
const isSelected = useAppSelector((s) => s.canvasV2.tool.selected === 'bbox');
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging;
|
||||
}, [isFiltering, isStaging, isTransforming]);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(toolChanged('bbox'));
|
||||
}, [dispatch]);
|
||||
|
||||
useHotkeys('q', onClick, { enabled: !isDisabled || isSelected }, [onClick, isSelected, isDisabled]);
|
||||
useHotkeys('q', selectBbox, { enabled: !isDisabled || isSelected }, [selectBbox, isSelected, isDisabled]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
@@ -32,7 +28,7 @@ export const ToolBboxButton = memo(() => {
|
||||
icon={<PiBoundingBoxBold />}
|
||||
colorScheme={isSelected ? 'invokeBlue' : 'base'}
|
||||
variant="outline"
|
||||
onClick={onClick}
|
||||
onClick={selectBbox}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,37 +1,33 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { toolChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { isDrawableEntityType } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiPaintBrushBold } from 'react-icons/pi';
|
||||
|
||||
export const ToolBrushButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isFiltering = useIsFiltering();
|
||||
const isTransforming = useIsTransforming();
|
||||
const isStaging = useAppSelector((s) => s.canvasV2.session.isStaging);
|
||||
const isSelected = useAppSelector((s) => s.canvasV2.tool.selected === 'brush');
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
const selectBrush = useSelectTool('brush');
|
||||
const isSelected = useToolIsSelected('brush');
|
||||
const isDrawingToolAllowed = useAppSelector((s) => {
|
||||
if (!s.canvasV2.selectedEntityIdentifier?.type) {
|
||||
if (!s.canvasV2.present.selectedEntityIdentifier?.type) {
|
||||
return false;
|
||||
}
|
||||
return isDrawableEntityType(s.canvasV2.selectedEntityIdentifier.type);
|
||||
return isDrawableEntityType(s.canvasV2.present.selectedEntityIdentifier.type);
|
||||
});
|
||||
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging || !isDrawingToolAllowed;
|
||||
}, [isDrawingToolAllowed, isFiltering, isStaging, isTransforming]);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(toolChanged('brush'));
|
||||
}, [dispatch]);
|
||||
|
||||
useHotkeys('b', onClick, { enabled: !isDisabled || isSelected }, [isDisabled, isSelected, onClick]);
|
||||
useHotkeys('b', selectBrush, { enabled: !isDisabled || isSelected }, [isDisabled, isSelected, selectBrush]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
@@ -40,7 +36,7 @@ export const ToolBrushButton = memo(() => {
|
||||
icon={<PiPaintBrushBold />}
|
||||
colorScheme={isSelected ? 'invokeBlue' : 'base'}
|
||||
variant="outline"
|
||||
onClick={onClick}
|
||||
onClick={selectBrush}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { brushWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { brushWidthChanged } from 'features/controlLayers/store/toolSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -20,7 +20,7 @@ const formatPx = (v: number | string) => `${v} px`;
|
||||
export const ToolBrushWidth = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const width = useAppSelector((s) => s.canvasV2.tool.brush.width);
|
||||
const width = useAppSelector((s) => s.tool.brush.width);
|
||||
const onChange = useCallback(
|
||||
(v: number) => {
|
||||
dispatch(brushWidthChanged(Math.round(v)));
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { toolChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiEyedropperBold } from 'react-icons/pi';
|
||||
|
||||
export const ToolColorPickerButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isFiltering = useIsFiltering();
|
||||
const isTransforming = useIsTransforming();
|
||||
const isSelected = useAppSelector((s) => s.canvasV2.tool.selected === 'colorPicker');
|
||||
const isStaging = useAppSelector((s) => s.canvasV2.session.isStaging);
|
||||
const selectColorPicker = useSelectTool('colorPicker');
|
||||
const isSelected = useToolIsSelected('colorPicker');
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging;
|
||||
}, [isFiltering, isStaging, isTransforming]);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(toolChanged('colorPicker'));
|
||||
}, [dispatch]);
|
||||
|
||||
useHotkeys('i', onClick, { enabled: !isDisabled || isSelected }, [onClick, isSelected, isDisabled]);
|
||||
useHotkeys('i', selectColorPicker, { enabled: !isDisabled || isSelected }, [
|
||||
selectColorPicker,
|
||||
isSelected,
|
||||
isDisabled,
|
||||
]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
@@ -33,7 +33,7 @@ export const ToolColorPickerButton = memo(() => {
|
||||
icon={<PiEyedropperBold />}
|
||||
colorScheme={isSelected ? 'invokeBlue' : 'base'}
|
||||
variant="outline"
|
||||
onClick={onClick}
|
||||
onClick={selectColorPicker}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,36 +1,32 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { toolChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { isDrawableEntityType } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiEraserBold } from 'react-icons/pi';
|
||||
|
||||
export const ToolEraserButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isFiltering = useIsFiltering();
|
||||
const isTransforming = useIsTransforming();
|
||||
const isStaging = useAppSelector((s) => s.canvasV2.session.isStaging);
|
||||
const isSelected = useAppSelector((s) => s.canvasV2.tool.selected === 'eraser');
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
const selectEraser = useSelectTool('eraser');
|
||||
const isSelected = useToolIsSelected('eraser');
|
||||
const isDrawingToolAllowed = useAppSelector((s) => {
|
||||
if (!s.canvasV2.selectedEntityIdentifier?.type) {
|
||||
if (!s.canvasV2.present.selectedEntityIdentifier?.type) {
|
||||
return false;
|
||||
}
|
||||
return isDrawableEntityType(s.canvasV2.selectedEntityIdentifier.type);
|
||||
return isDrawableEntityType(s.canvasV2.present.selectedEntityIdentifier.type);
|
||||
});
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging || !isDrawingToolAllowed;
|
||||
}, [isDrawingToolAllowed, isFiltering, isStaging, isTransforming]);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(toolChanged('eraser'));
|
||||
}, [dispatch]);
|
||||
|
||||
useHotkeys('e', onClick, { enabled: !isDisabled || isSelected }, [isDisabled, isSelected, onClick]);
|
||||
useHotkeys('e', selectEraser, { enabled: !isDisabled || isSelected }, [isDisabled, isSelected, selectEraser]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
@@ -39,7 +35,7 @@ export const ToolEraserButton = memo(() => {
|
||||
icon={<PiEraserBold />}
|
||||
colorScheme={isSelected ? 'invokeBlue' : 'base'}
|
||||
variant="outline"
|
||||
onClick={onClick}
|
||||
onClick={selectEraser}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { eraserWidthChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { eraserWidthChanged } from 'features/controlLayers/store/toolSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -20,7 +20,7 @@ const formatPx = (v: number | string) => `${v} px`;
|
||||
export const ToolEraserWidth = memo(() => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const width = useAppSelector((s) => s.canvasV2.tool.eraser.width);
|
||||
const width = useAppSelector((s) => s.tool.eraser.width);
|
||||
const onChange = useCallback(
|
||||
(v: number) => {
|
||||
dispatch(eraserWidthChanged(Math.round(v)));
|
||||
|
||||
@@ -2,14 +2,14 @@ import { Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@inv
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { fillChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { fillChanged } from 'features/controlLayers/store/toolSlice';
|
||||
import type { RgbaColor } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const ToolFillColorPicker = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const fill = useAppSelector((s) => s.canvasV2.tool.fill);
|
||||
const fill = useAppSelector((s) => s.tool.fill);
|
||||
const dispatch = useAppDispatch();
|
||||
const onChange = useCallback(
|
||||
(color: RgbaColor) => {
|
||||
|
||||
@@ -1,36 +1,32 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { toolChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { isDrawableEntityType } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiCursorBold } from 'react-icons/pi';
|
||||
|
||||
export const ToolMoveButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isFiltering = useIsFiltering();
|
||||
const isTransforming = useIsTransforming();
|
||||
const isSelected = useAppSelector((s) => s.canvasV2.tool.selected === 'move');
|
||||
const isStaging = useAppSelector((s) => s.canvasV2.session.isStaging);
|
||||
const selectMove = useSelectTool('move');
|
||||
const isSelected = useToolIsSelected('move');
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
const isDrawingToolAllowed = useAppSelector((s) => {
|
||||
if (!s.canvasV2.selectedEntityIdentifier?.type) {
|
||||
if (!s.canvasV2.present.selectedEntityIdentifier?.type) {
|
||||
return false;
|
||||
}
|
||||
return isDrawableEntityType(s.canvasV2.selectedEntityIdentifier.type);
|
||||
return isDrawableEntityType(s.canvasV2.present.selectedEntityIdentifier.type);
|
||||
});
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging || !isDrawingToolAllowed;
|
||||
}, [isDrawingToolAllowed, isFiltering, isStaging, isTransforming]);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(toolChanged('move'));
|
||||
}, [dispatch]);
|
||||
|
||||
useHotkeys('v', onClick, { enabled: !isDisabled || isSelected }, [isDisabled, isSelected, onClick]);
|
||||
useHotkeys('v', selectMove, { enabled: !isDisabled || isSelected }, [isDisabled, isSelected, selectMove]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
@@ -39,7 +35,7 @@ export const ToolMoveButton = memo(() => {
|
||||
icon={<PiCursorBold />}
|
||||
colorScheme={isSelected ? 'invokeBlue' : 'base'}
|
||||
variant="outline"
|
||||
onClick={onClick}
|
||||
onClick={selectMove}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,37 +1,33 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { toolChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { isDrawableEntityType } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiRectangleBold } from 'react-icons/pi';
|
||||
|
||||
export const ToolRectButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isSelected = useAppSelector((s) => s.canvasV2.tool.selected === 'rect');
|
||||
const selectRect = useSelectTool('rect');
|
||||
const isSelected = useToolIsSelected('rect');
|
||||
const isFiltering = useIsFiltering();
|
||||
const isTransforming = useIsTransforming();
|
||||
const isStaging = useAppSelector((s) => s.canvasV2.session.isStaging);
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
const isDrawingToolAllowed = useAppSelector((s) => {
|
||||
if (!s.canvasV2.selectedEntityIdentifier?.type) {
|
||||
if (!s.canvasV2.present.selectedEntityIdentifier?.type) {
|
||||
return false;
|
||||
}
|
||||
return isDrawableEntityType(s.canvasV2.selectedEntityIdentifier.type);
|
||||
return isDrawableEntityType(s.canvasV2.present.selectedEntityIdentifier.type);
|
||||
});
|
||||
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging || !isDrawingToolAllowed;
|
||||
}, [isDrawingToolAllowed, isFiltering, isStaging, isTransforming]);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(toolChanged('rect'));
|
||||
}, [dispatch]);
|
||||
|
||||
useHotkeys('u', onClick, { enabled: !isDisabled || isSelected }, [isDisabled, isSelected, onClick]);
|
||||
useHotkeys('u', selectRect, { enabled: !isDisabled || isSelected }, [isDisabled, isSelected, selectRect]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
@@ -40,7 +36,7 @@ export const ToolRectButton = memo(() => {
|
||||
icon={<PiRectangleBold />}
|
||||
colorScheme={isSelected ? 'invokeBlue' : 'base'}
|
||||
variant="outline"
|
||||
onClick={onClick}
|
||||
onClick={selectRect}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { ToolBrushWidth } from 'features/controlLayers/components/Tool/ToolBrushWidth';
|
||||
import { ToolEraserWidth } from 'features/controlLayers/components/Tool/ToolEraserWidth';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const ToolSettings = memo(() => {
|
||||
const canvasManager = useCanvasManager();
|
||||
const tool = useStore(canvasManager.stateApi.$tool);
|
||||
if (tool === 'brush') {
|
||||
return <ToolBrushWidth />;
|
||||
}
|
||||
if (tool === 'eraser') {
|
||||
return <ToolEraserWidth />;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
ToolSettings.displayName = 'ToolSettings';
|
||||
@@ -1,28 +1,25 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
|
||||
import { useIsFiltering } from 'features/controlLayers/hooks/useIsFiltering';
|
||||
import { useIsTransforming } from 'features/controlLayers/hooks/useIsTransforming';
|
||||
import { toolChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiHandBold } from 'react-icons/pi';
|
||||
|
||||
export const ToolViewButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const isTransforming = useIsTransforming();
|
||||
const isFiltering = useIsFiltering();
|
||||
const isStaging = useAppSelector((s) => s.canvasV2.session.isStaging);
|
||||
const isSelected = useAppSelector((s) => s.canvasV2.tool.selected === 'view');
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
const selectView = useSelectTool('view');
|
||||
const isSelected = useToolIsSelected('view');
|
||||
const isDisabled = useMemo(() => {
|
||||
return isTransforming || isFiltering || isStaging;
|
||||
}, [isFiltering, isStaging, isTransforming]);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(toolChanged('view'));
|
||||
}, [dispatch]);
|
||||
|
||||
useHotkeys('h', onClick, { enabled: !isDisabled || isSelected }, [onClick, isSelected, isDisabled]);
|
||||
useHotkeys('h', selectView, { enabled: !isDisabled || isSelected }, [selectView, isSelected, isDisabled]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
@@ -31,7 +28,7 @@ export const ToolViewButton = memo(() => {
|
||||
icon={<PiHandBold />}
|
||||
colorScheme={isSelected ? 'invokeBlue' : 'base'}
|
||||
variant="outline"
|
||||
onClick={onClick}
|
||||
onClick={selectView}
|
||||
isDisabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import type { Tool } from 'features/controlLayers/store/types';
|
||||
import { computed } from 'nanostores';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useToolIsSelected = (tool: Tool) => {
|
||||
const canvasManager = useCanvasManager();
|
||||
const isSelected = useStore(computed(canvasManager.stateApi.$tool, (t) => t === tool));
|
||||
return isSelected;
|
||||
};
|
||||
|
||||
export const useSelectTool = (tool: Tool) => {
|
||||
const canvasManager = useCanvasManager();
|
||||
const setTool = useCallback(() => {
|
||||
canvasManager.stateApi.$tool.set(tool);
|
||||
}, [canvasManager.stateApi.$tool, tool]);
|
||||
return setTool;
|
||||
};
|
||||
@@ -5,22 +5,25 @@ import { memo, useCallback } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiArrowClockwiseBold, PiArrowCounterClockwiseBold } from 'react-icons/pi';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { ActionCreators } from 'redux-undo';
|
||||
|
||||
export const UndoRedoButtonGroup = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const mayUndo = useAppSelector(() => false);
|
||||
const mayUndo = useAppSelector(() => true);
|
||||
const handleUndo = useCallback(() => {
|
||||
// TODO(psyche): Implement undo
|
||||
// dispatch(undo());
|
||||
}, []);
|
||||
dispatch(ActionCreators.undo());
|
||||
}, [dispatch]);
|
||||
useHotkeys(['meta+z', 'ctrl+z'], handleUndo, { enabled: mayUndo, preventDefault: true }, [mayUndo, handleUndo]);
|
||||
|
||||
const mayRedo = useAppSelector(() => false);
|
||||
const mayRedo = useAppSelector(() => true);
|
||||
const handleRedo = useCallback(() => {
|
||||
// TODO(psyche): Implement redo
|
||||
// dispatch(redo());
|
||||
}, []);
|
||||
dispatch(ActionCreators.redo());
|
||||
}, [dispatch]);
|
||||
useHotkeys(['meta+shift+z', 'ctrl+shift+z'], handleRedo, { enabled: mayRedo, preventDefault: true }, [
|
||||
mayRedo,
|
||||
handleRedo,
|
||||
|
||||
@@ -56,7 +56,7 @@ export const CanvasEntityHeader = memo(({ children, ...rest }: FlexProps) => {
|
||||
}, [entityIdentifier]);
|
||||
|
||||
return (
|
||||
<ContextMenu renderMenu={renderMenu}>
|
||||
<ContextMenu renderMenu={renderMenu} stopImmediatePropagation>
|
||||
{(ref) => (
|
||||
<Flex ref={ref} gap={2} alignItems="center" p={2} {...rest}>
|
||||
{children}
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
entityArrangedForwardOne,
|
||||
entityArrangedToBack,
|
||||
entityArrangedToFront,
|
||||
selectCanvasV2Slice,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier, CanvasV2State } from 'features/controlLayers/store/types';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -15,7 +15,8 @@ import {
|
||||
} from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { snapToNearest } from 'features/controlLayers/konva/util';
|
||||
import { entityOpacityChanged, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityOpacityChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { isDrawableEntity } from 'features/controlLayers/store/types';
|
||||
import { clamp, round } from 'lodash-es';
|
||||
import type { KeyboardEvent } from 'react';
|
||||
@@ -58,13 +59,13 @@ const snapCandidates = marks.slice(1, marks.length - 1);
|
||||
export const CanvasEntityOpacity = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const selectedEntityIdentifier = useAppSelector((s) => s.canvasV2.selectedEntityIdentifier);
|
||||
const selectedEntityIdentifier = useAppSelector((s) => s.canvasV2.present.selectedEntityIdentifier);
|
||||
const opacity = useAppSelector((s) => {
|
||||
const selectedEntityIdentifier = s.canvasV2.selectedEntityIdentifier;
|
||||
const selectedEntityIdentifier = s.canvasV2.present.selectedEntityIdentifier;
|
||||
if (!selectedEntityIdentifier) {
|
||||
return null;
|
||||
}
|
||||
const selectedEntity = selectEntity(s.canvasV2, selectedEntityIdentifier);
|
||||
const selectedEntity = selectEntity(s.canvasV2.present, selectedEntityIdentifier);
|
||||
if (!selectedEntity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { useEntityAdapter } from 'features/controlLayers/contexts/EntityAdapterContext';
|
||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { memo, useEffect, useMemo, useRef } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import type { CanvasEntityIdentifier, CanvasEntityType } from 'features/controlLayers/store/types';
|
||||
import { createContext, useContext } from 'react';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export const EntityIdentifierContext = createContext<CanvasEntityIdentifier | null>(null);
|
||||
|
||||
export const useEntityIdentifierContext = (): CanvasEntityIdentifier => {
|
||||
export const useEntityIdentifierContext = <T extends CanvasEntityType | undefined = CanvasEntityType>(
|
||||
type?: T
|
||||
): CanvasEntityIdentifier<T extends undefined ? CanvasEntityType : T> => {
|
||||
const entityIdentifier = useContext(EntityIdentifierContext);
|
||||
assert(entityIdentifier, 'useEntityIdentifier must be used within a EntityIdentifierProvider');
|
||||
return entityIdentifier;
|
||||
if (type) {
|
||||
assert(entityIdentifier.type === type, 'useEntityIdentifier must be used with the correct type');
|
||||
}
|
||||
return entityIdentifier as CanvasEntityIdentifier<T extends undefined ? CanvasEntityType : T>;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { entityDeleted, selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityDeleted } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
@@ -14,7 +15,7 @@ export function useCanvasDeleteLayerHotkey() {
|
||||
useAssertSingleton(useCanvasDeleteLayerHotkey.name);
|
||||
const dispatch = useAppDispatch();
|
||||
const selectedEntityIdentifier = useAppSelector(selectSelectedEntityIdentifier);
|
||||
const isStaging = useAppSelector((s) => s.canvasV2.session.isStaging);
|
||||
const isStaging = useAppSelector((s) => s.canvasSession.isStaging);
|
||||
|
||||
const deleteSelectedLayer = useCallback(() => {
|
||||
if (selectedEntityIdentifier === null) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { entityReset, selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { entityReset } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types'
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useEntityIsSelected = (entityIdentifier: CanvasEntityIdentifier) => {
|
||||
const selectedEntityIdentifier = useAppSelector((s) => s.canvasV2.selectedEntityIdentifier);
|
||||
const selectedEntityIdentifier = useAppSelector((s) => s.canvasV2.present.selectedEntityIdentifier);
|
||||
const isSelected = useMemo(() => {
|
||||
return selectedEntityIdentifier?.id === entityIdentifier.id;
|
||||
}, [selectedEntityIdentifier, entityIdentifier.id]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import { type CanvasEntityIdentifier, isDrawableEntity } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEntityObjectCount } from 'features/controlLayers/hooks/useEntityObjectCount';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice, selectEntity } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { createMemoizedAppSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { selectControlLayerEntityOrThrow } from 'features/controlLayers/store/controlLayersReducers';
|
||||
import { selectCanvasV2Slice, selectEntityOrThrow } from 'features/controlLayers/store/selectors';
|
||||
import type {
|
||||
CanvasEntityIdentifier,
|
||||
ControlNetConfig,
|
||||
@@ -14,11 +13,11 @@ import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { useMemo } from 'react';
|
||||
import { useControlNetAndT2IAdapterModels, useIPAdapterModels } from 'services/api/hooks/modelsByType';
|
||||
|
||||
export const useControlLayerControlAdapter = (entityIdentifier: CanvasEntityIdentifier) => {
|
||||
export const useControlLayerControlAdapter = (entityIdentifier: CanvasEntityIdentifier<'control_layer'>) => {
|
||||
const selectControlAdapter = useMemo(
|
||||
() =>
|
||||
createMemoizedAppSelector(selectCanvasV2Slice, (canvasV2) => {
|
||||
const layer = selectControlLayerEntityOrThrow(canvasV2, entityIdentifier.id);
|
||||
const layer = selectEntityOrThrow(canvasV2, entityIdentifier);
|
||||
return layer.controlAdapter;
|
||||
}),
|
||||
[entityIdentifier]
|
||||
@@ -31,7 +30,7 @@ export const useControlLayerControlAdapter = (entityIdentifier: CanvasEntityIden
|
||||
export const useDefaultControlAdapter = (): ControlNetConfig | T2IAdapterConfig => {
|
||||
const [modelConfigs] = useControlNetAndT2IAdapterModels();
|
||||
|
||||
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
||||
const baseModel = useAppSelector((s) => s.params.model?.base);
|
||||
|
||||
const defaultControlAdapter = useMemo(() => {
|
||||
const compatibleModels = modelConfigs.filter((m) => (baseModel ? m.base === baseModel : true));
|
||||
@@ -52,7 +51,7 @@ export const useDefaultControlAdapter = (): ControlNetConfig | T2IAdapterConfig
|
||||
export const useDefaultIPAdapter = (): IPAdapterConfig => {
|
||||
const [modelConfigs] = useIPAdapterModels();
|
||||
|
||||
const baseModel = useAppSelector((s) => s.canvasV2.params.model?.base);
|
||||
const baseModel = useAppSelector((s) => s.params.model?.base);
|
||||
|
||||
const defaultControlAdapter = useMemo(() => {
|
||||
const compatibleModels = modelConfigs.filter((m) => (baseModel ? m.base === baseModel : true));
|
||||
|
||||
@@ -1,29 +1,35 @@
|
||||
import { getArbitraryBaseColor } from '@invoke-ai/ui-library';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import Konva from 'konva';
|
||||
import type { Logger } from 'roarr';
|
||||
|
||||
export class CanvasBackgroundModule {
|
||||
readonly type = 'background_grid';
|
||||
export class CanvasBackgroundModule extends CanvasModuleBase {
|
||||
readonly type = 'background';
|
||||
|
||||
static GRID_LINE_COLOR_COARSE = getArbitraryBaseColor(27);
|
||||
static GRID_LINE_COLOR_FINE = getArbitraryBaseColor(18);
|
||||
|
||||
id: string;
|
||||
path: string[];
|
||||
manager: CanvasManager;
|
||||
subscriptions = new Set<() => void>();
|
||||
log: Logger;
|
||||
|
||||
konva: {
|
||||
layer: Konva.Layer;
|
||||
};
|
||||
|
||||
/**
|
||||
* A set of subscriptions that should be cleaned up when the transformer is destroyed.
|
||||
*/
|
||||
subscriptions: Set<() => void> = new Set();
|
||||
|
||||
constructor(manager: CanvasManager) {
|
||||
super();
|
||||
this.id = getPrefixedId(this.type);
|
||||
this.manager = manager;
|
||||
this.path = this.manager.path.concat(this.id);
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
|
||||
this.log.debug('Creating background module');
|
||||
|
||||
this.konva = { layer: new Konva.Layer({ name: `${this.type}:layer`, listening: false }) };
|
||||
|
||||
this.subscriptions.add(
|
||||
@@ -116,9 +122,8 @@ export class CanvasBackgroundModule {
|
||||
}
|
||||
|
||||
destroy = () => {
|
||||
for (const cleanup of this.subscriptions) {
|
||||
cleanup();
|
||||
}
|
||||
this.log.trace('Destroying background module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
this.konva.layer.destroy();
|
||||
};
|
||||
|
||||
@@ -145,4 +150,16 @@ export class CanvasBackgroundModule {
|
||||
}
|
||||
return 256;
|
||||
};
|
||||
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
path: this.path,
|
||||
type: this.type,
|
||||
};
|
||||
};
|
||||
|
||||
getLoggingContext = () => {
|
||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { roundToMultiple, roundToMultipleMin } from 'common/util/roundDownToMultiple';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import type { CanvasPreviewModule } from 'features/controlLayers/konva/CanvasPreviewModule';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import type { Rect } from 'features/controlLayers/store/types';
|
||||
@@ -22,14 +23,16 @@ const ALL_ANCHORS: string[] = [
|
||||
const CORNER_ANCHORS: string[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
||||
const NO_ANCHORS: string[] = [];
|
||||
|
||||
export class CanvasBboxModule {
|
||||
readonly type = 'generation_bbox';
|
||||
export class CanvasBboxModule extends CanvasModuleBase {
|
||||
readonly type = 'bbox';
|
||||
|
||||
id: string;
|
||||
path: string[];
|
||||
parent: CanvasPreviewModule;
|
||||
manager: CanvasManager;
|
||||
log: Logger;
|
||||
subscriptions: Set<() => void> = new Set();
|
||||
|
||||
parent: CanvasPreviewModule;
|
||||
|
||||
konva: {
|
||||
group: Konva.Group;
|
||||
@@ -38,13 +41,14 @@ export class CanvasBboxModule {
|
||||
};
|
||||
|
||||
constructor(parent: CanvasPreviewModule) {
|
||||
super();
|
||||
this.id = getPrefixedId(this.type);
|
||||
this.parent = parent;
|
||||
this.manager = this.parent.manager;
|
||||
this.path = this.manager.path.concat(this.id);
|
||||
this.path = this.parent.path.concat(this.id);
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
|
||||
this.log.trace('Creating bbox');
|
||||
this.log.debug('Creating bbox module');
|
||||
|
||||
// Create a stash to hold onto the last aspect ratio of the bbox - this allows for locking the aspect ratio when
|
||||
// transforming the bbox.
|
||||
@@ -228,17 +232,19 @@ export class CanvasBboxModule {
|
||||
this.konva.transformer.nodes([this.konva.rect]);
|
||||
this.konva.group.add(this.konva.rect);
|
||||
this.konva.group.add(this.konva.transformer);
|
||||
|
||||
this.subscriptions.add(this.manager.stateApi.$tool.listen(this.render));
|
||||
}
|
||||
|
||||
render() {
|
||||
this.log.trace('Rendering generation bbox');
|
||||
render = () => {
|
||||
this.log.trace('Rendering bbox module');
|
||||
|
||||
const bbox = this.manager.stateApi.getBbox();
|
||||
const toolState = this.manager.stateApi.getToolState();
|
||||
const tool = this.manager.stateApi.$tool.get();
|
||||
|
||||
this.konva.group.visible(true);
|
||||
this.parent.getLayer().listening(toolState.selected === 'bbox');
|
||||
this.konva.group.listening(toolState.selected === 'bbox');
|
||||
this.parent.getLayer().listening(tool === 'bbox');
|
||||
this.konva.group.listening(tool === 'bbox');
|
||||
this.konva.rect.setAttrs({
|
||||
x: bbox.rect.x,
|
||||
y: bbox.rect.y,
|
||||
@@ -246,15 +252,29 @@ export class CanvasBboxModule {
|
||||
height: bbox.rect.height,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
listening: toolState.selected === 'bbox',
|
||||
listening: tool === 'bbox',
|
||||
});
|
||||
this.konva.transformer.setAttrs({
|
||||
listening: toolState.selected === 'bbox',
|
||||
enabledAnchors: toolState.selected === 'bbox' ? ALL_ANCHORS : NO_ANCHORS,
|
||||
listening: tool === 'bbox',
|
||||
enabledAnchors: tool === 'bbox' ? ALL_ANCHORS : NO_ANCHORS,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
};
|
||||
};
|
||||
|
||||
destroy = () => {
|
||||
this.log.trace('Destroying bbox module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
this.konva.group.destroy();
|
||||
};
|
||||
|
||||
getLoggingContext = (): SerializableObject => {
|
||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||
import type { CanvasBrushLineState } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import type { Logger } from 'roarr';
|
||||
|
||||
export class CanvasBrushLineRenderer {
|
||||
export class CanvasBrushLineRenderer extends CanvasModuleBase {
|
||||
readonly type = 'brush_line_renderer';
|
||||
|
||||
id: string;
|
||||
@@ -15,6 +15,7 @@ export class CanvasBrushLineRenderer {
|
||||
parent: CanvasObjectRenderer;
|
||||
manager: CanvasManager;
|
||||
log: Logger;
|
||||
subscriptions = new Set<() => void>();
|
||||
|
||||
state: CanvasBrushLineState;
|
||||
konva: {
|
||||
@@ -23,6 +24,7 @@ export class CanvasBrushLineRenderer {
|
||||
};
|
||||
|
||||
constructor(state: CanvasBrushLineState, parent: CanvasObjectRenderer) {
|
||||
super();
|
||||
const { id, clip } = state;
|
||||
this.id = id;
|
||||
this.parent = parent;
|
||||
@@ -30,7 +32,7 @@ export class CanvasBrushLineRenderer {
|
||||
this.path = this.parent.path.concat(this.id);
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
|
||||
this.log.trace({ state }, 'Creating brush line');
|
||||
this.log.debug({ state }, 'Creating brush line renderer module');
|
||||
|
||||
this.konva = {
|
||||
group: new Konva.Group({
|
||||
@@ -69,26 +71,28 @@ export class CanvasBrushLineRenderer {
|
||||
return false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.log.trace('Destroying brush line');
|
||||
destroy = () => {
|
||||
this.log.debug('Destroying brush line renderer module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
this.konva.group.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
setVisibility(isVisible: boolean): void {
|
||||
this.log.trace({ isVisible }, 'Setting brush line visibility');
|
||||
this.konva.group.visible(isVisible);
|
||||
}
|
||||
|
||||
repr() {
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
parent: this.parent.id,
|
||||
state: deepClone(this.state),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
getLoggingContext = (): SerializableObject => {
|
||||
getLoggingContext = () => {
|
||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import type { GenerationMode } from 'features/controlLayers/store/types';
|
||||
import { LRUCache } from 'lru-cache';
|
||||
import type { Logger } from 'roarr';
|
||||
|
||||
export class CanvasCacheModule {
|
||||
export class CanvasCacheModule extends CanvasModuleBase {
|
||||
readonly type = 'cache';
|
||||
|
||||
id: string;
|
||||
path: string[];
|
||||
log: Logger;
|
||||
manager: CanvasManager;
|
||||
subscriptions = new Set<() => void>();
|
||||
|
||||
imageNameCache = new LRUCache<string, string>({ max: 100 });
|
||||
canvasElementCache = new LRUCache<string, HTMLCanvasElement>({ max: 32 });
|
||||
generationModeCache = new LRUCache<string, GenerationMode>({ max: 100 });
|
||||
|
||||
constructor(manager: CanvasManager) {
|
||||
super();
|
||||
this.id = getPrefixedId('cache');
|
||||
this.manager = manager;
|
||||
this.path = this.manager.path.concat(this.id);
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
this.log.debug('Creating canvas cache');
|
||||
|
||||
this.log.debug('Creating cache module');
|
||||
}
|
||||
|
||||
clearAll = () => {
|
||||
@@ -29,7 +34,21 @@ export class CanvasCacheModule {
|
||||
this.generationModeCache.clear();
|
||||
};
|
||||
|
||||
getLoggingContext = (): SerializableObject => {
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
path: this.path,
|
||||
type: this.type,
|
||||
};
|
||||
};
|
||||
|
||||
destroy = () => {
|
||||
this.log.debug('Destroying cache module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
this.clearAll();
|
||||
};
|
||||
|
||||
getLoggingContext = () => {
|
||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import {
|
||||
canvasToBlob,
|
||||
canvasToImageData,
|
||||
@@ -14,18 +15,22 @@ import type { ImageDTO } from 'services/api/types';
|
||||
import stableHash from 'stable-hash';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export class CanvasCompositorModule {
|
||||
export class CanvasCompositorModule extends CanvasModuleBase {
|
||||
readonly type = 'compositor';
|
||||
|
||||
id: string;
|
||||
path: string[];
|
||||
log: Logger;
|
||||
manager: CanvasManager;
|
||||
subscriptions = new Set<() => void>();
|
||||
|
||||
constructor(manager: CanvasManager) {
|
||||
super();
|
||||
this.id = getPrefixedId('canvas_compositor');
|
||||
this.manager = manager;
|
||||
this.path = this.manager.path.concat(this.id);
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
this.log.debug('Creating canvas compositor');
|
||||
this.log.debug('Creating compositor module');
|
||||
}
|
||||
|
||||
getCompositeRasterLayerEntityIds = (): string[] => {
|
||||
@@ -237,7 +242,20 @@ export class CanvasCompositorModule {
|
||||
return generationMode;
|
||||
}
|
||||
|
||||
getLoggingContext = (): SerializableObject => {
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
};
|
||||
};
|
||||
|
||||
destroy = () => {
|
||||
this.log.trace('Destroying compositor module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
};
|
||||
|
||||
getLoggingContext = () => {
|
||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||
import type { CanvasEraserLineState } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import type { Logger } from 'roarr';
|
||||
|
||||
export class CanvasEraserLineRenderer {
|
||||
export class CanvasEraserLineRenderer extends CanvasModuleBase {
|
||||
readonly type = 'eraser_line_renderer';
|
||||
|
||||
id: string;
|
||||
@@ -14,6 +14,7 @@ export class CanvasEraserLineRenderer {
|
||||
parent: CanvasObjectRenderer;
|
||||
manager: CanvasManager;
|
||||
log: Logger;
|
||||
subscriptions = new Set<() => void>();
|
||||
|
||||
state: CanvasEraserLineState;
|
||||
konva: {
|
||||
@@ -22,6 +23,7 @@ export class CanvasEraserLineRenderer {
|
||||
};
|
||||
|
||||
constructor(state: CanvasEraserLineState, parent: CanvasObjectRenderer) {
|
||||
super();
|
||||
const { id, clip } = state;
|
||||
this.id = id;
|
||||
this.parent = parent;
|
||||
@@ -29,7 +31,7 @@ export class CanvasEraserLineRenderer {
|
||||
this.path = this.parent.path.concat(this.id);
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
|
||||
this.log.trace({ state }, 'Creating eraser line');
|
||||
this.log.debug({ state }, 'Creating eraser line renderer module');
|
||||
|
||||
this.konva = {
|
||||
group: new Konva.Group({
|
||||
@@ -68,26 +70,28 @@ export class CanvasEraserLineRenderer {
|
||||
return false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.log.trace('Destroying eraser line');
|
||||
destroy = () => {
|
||||
this.log.debug('Destroying eraser line renderer module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
this.konva.group.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
setVisibility(isVisible: boolean): void {
|
||||
this.log.trace({ isVisible }, 'Setting brush line visibility');
|
||||
this.konva.group.visible(isVisible);
|
||||
}
|
||||
|
||||
repr() {
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
parent: this.parent.id,
|
||||
state: deepClone(this.state),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
getLoggingContext = (): SerializableObject => {
|
||||
getLoggingContext = () => {
|
||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import type { CanvasEntityIdentifier, CanvasImageState, FilterConfig } from 'features/controlLayers/store/types';
|
||||
import { IMAGE_FILTERS, imageDTOToImageObject } from 'features/controlLayers/store/types';
|
||||
@@ -10,15 +11,14 @@ import { getImageDTO } from 'services/api/endpoints/images';
|
||||
import type { BatchConfig, ImageDTO, S } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const TYPE = 'entity_filter_preview';
|
||||
|
||||
export class CanvasFilterModule {
|
||||
readonly type = TYPE;
|
||||
export class CanvasFilterModule extends CanvasModuleBase {
|
||||
readonly type = 'canvas_filter';
|
||||
|
||||
id: string;
|
||||
path: string[];
|
||||
manager: CanvasManager;
|
||||
log: Logger;
|
||||
subscriptions = new Set<() => void>();
|
||||
|
||||
imageState: CanvasImageState | null = null;
|
||||
|
||||
@@ -28,11 +28,13 @@ export class CanvasFilterModule {
|
||||
$config = atom<FilterConfig>(IMAGE_FILTERS.canny_image_processor.buildDefaults());
|
||||
|
||||
constructor(manager: CanvasManager) {
|
||||
super();
|
||||
this.id = getPrefixedId(this.type);
|
||||
this.manager = manager;
|
||||
this.path = this.manager.path.concat(this.id);
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
this.log.trace('Creating filter');
|
||||
|
||||
this.log.debug('Creating filter module');
|
||||
}
|
||||
|
||||
initialize = (entityIdentifier: CanvasEntityIdentifier) => {
|
||||
@@ -47,7 +49,7 @@ export class CanvasFilterModule {
|
||||
return;
|
||||
}
|
||||
this.$adapter.set(entity.adapter);
|
||||
this.manager.stateApi.setTool('view');
|
||||
this.manager.stateApi.$tool.set('view');
|
||||
};
|
||||
|
||||
previewFilter = async () => {
|
||||
@@ -167,17 +169,19 @@ export class CanvasFilterModule {
|
||||
};
|
||||
|
||||
destroy = () => {
|
||||
this.log.trace('Destroying filter');
|
||||
this.log.trace('Destroying filter module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
};
|
||||
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
};
|
||||
};
|
||||
|
||||
getLoggingContext = (): SerializableObject => {
|
||||
getLoggingContext = () => {
|
||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Mutex } from 'async-mutex';
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasFilterModule } from 'features/controlLayers/konva/CanvasFilterModule';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||
import type { CanvasStagingAreaModule } from 'features/controlLayers/konva/CanvasStagingAreaModule';
|
||||
import { loadImage } from 'features/controlLayers/konva/util';
|
||||
@@ -12,7 +12,7 @@ import Konva from 'konva';
|
||||
import type { Logger } from 'roarr';
|
||||
import { getImageDTO } from 'services/api/endpoints/images';
|
||||
|
||||
export class CanvasImageRenderer {
|
||||
export class CanvasImageRenderer extends CanvasModuleBase {
|
||||
readonly type = 'image_renderer';
|
||||
|
||||
id: string;
|
||||
@@ -20,6 +20,7 @@ export class CanvasImageRenderer {
|
||||
parent: CanvasObjectRenderer | CanvasStagingAreaModule | CanvasFilterModule;
|
||||
manager: CanvasManager;
|
||||
log: Logger;
|
||||
subscriptions = new Set<() => void>();
|
||||
|
||||
state: CanvasImageState;
|
||||
konva: {
|
||||
@@ -33,6 +34,7 @@ export class CanvasImageRenderer {
|
||||
mutex = new Mutex();
|
||||
|
||||
constructor(state: CanvasImageState, parent: CanvasObjectRenderer | CanvasStagingAreaModule | CanvasFilterModule) {
|
||||
super();
|
||||
const { id, image } = state;
|
||||
const { width, height } = image;
|
||||
this.id = id;
|
||||
@@ -41,7 +43,7 @@ export class CanvasImageRenderer {
|
||||
this.path = this.parent.path.concat(this.id);
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
|
||||
this.log.trace({ state }, 'Creating image');
|
||||
this.log.debug({ state }, 'Creating image renderer module');
|
||||
|
||||
this.konva = {
|
||||
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
||||
@@ -166,7 +168,8 @@ export class CanvasImageRenderer {
|
||||
};
|
||||
|
||||
destroy = () => {
|
||||
this.log.trace('Destroying image');
|
||||
this.log.debug('Destroying image renderer module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
this.konva.group.destroy();
|
||||
};
|
||||
|
||||
@@ -179,6 +182,7 @@ export class CanvasImageRenderer {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
parent: this.parent.id,
|
||||
isLoading: this.isLoading,
|
||||
isError: this.isError,
|
||||
@@ -186,7 +190,7 @@ export class CanvasImageRenderer {
|
||||
};
|
||||
};
|
||||
|
||||
getLoggingContext = (): SerializableObject => {
|
||||
getLoggingContext = () => {
|
||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||
import { CanvasTransformer } from 'features/controlLayers/konva/CanvasTransformer';
|
||||
import { getLastPointOfLine } from 'features/controlLayers/konva/util';
|
||||
import type {
|
||||
CanvasBrushLineState,
|
||||
CanvasControlLayerState,
|
||||
CanvasEntityIdentifier,
|
||||
CanvasEraserLineState,
|
||||
CanvasRasterLayerState,
|
||||
CanvasV2State,
|
||||
Coordinate,
|
||||
Rect,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
@@ -18,12 +22,13 @@ import type { Logger } from 'roarr';
|
||||
import stableHash from 'stable-hash';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export class CanvasLayerAdapter {
|
||||
export class CanvasLayerAdapter extends CanvasModuleBase {
|
||||
readonly type = 'layer_adapter';
|
||||
|
||||
id: string;
|
||||
path: string[];
|
||||
manager: CanvasManager;
|
||||
subscriptions = new Set<() => void>();
|
||||
log: Logger;
|
||||
|
||||
state: CanvasRasterLayerState | CanvasControlLayerState;
|
||||
@@ -37,11 +42,14 @@ export class CanvasLayerAdapter {
|
||||
isFirstRender: boolean = true;
|
||||
|
||||
constructor(state: CanvasLayerAdapter['state'], manager: CanvasLayerAdapter['manager']) {
|
||||
super();
|
||||
this.id = state.id;
|
||||
this.manager = manager;
|
||||
this.path = this.manager.path.concat(this.id);
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
this.log.debug({ state }, 'Creating layer');
|
||||
|
||||
this.log.debug({ state }, 'Creating layer adapter module');
|
||||
|
||||
this.state = state;
|
||||
|
||||
this.konva = {
|
||||
@@ -67,18 +75,14 @@ export class CanvasLayerAdapter {
|
||||
};
|
||||
|
||||
destroy = (): void => {
|
||||
this.log.debug('Destroying layer');
|
||||
// We need to call the destroy method on all children so they can do their own cleanup.
|
||||
this.transformer.destroy();
|
||||
this.log.debug('Destroying layer adapter module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
this.renderer.destroy();
|
||||
this.transformer.destroy();
|
||||
this.konva.layer.destroy();
|
||||
};
|
||||
|
||||
update = async (arg?: {
|
||||
state: CanvasLayerAdapter['state'];
|
||||
toolState: CanvasV2State['tool'];
|
||||
isSelected: boolean;
|
||||
}) => {
|
||||
update = async (arg?: { state: CanvasLayerAdapter['state'] }) => {
|
||||
const state = get(arg, 'state', this.state);
|
||||
|
||||
if (!this.isFirstRender && state === this.state) {
|
||||
@@ -140,6 +144,7 @@ export class CanvasLayerAdapter {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
state: deepClone(this.state),
|
||||
transformer: this.transformer.repr(),
|
||||
renderer: this.renderer.repr(),
|
||||
@@ -180,6 +185,19 @@ export class CanvasLayerAdapter {
|
||||
return stableHash(arg);
|
||||
};
|
||||
|
||||
getLastPointOfLastLine = (type: CanvasBrushLineState['type'] | CanvasEraserLineState['type']): Coordinate | null => {
|
||||
const lastObject = this.state.objects[this.state.objects.length - 1];
|
||||
if (!lastObject) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (lastObject.type === type) {
|
||||
return getLastPointOfLine(lastObject.points);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
logDebugInfo(msg = 'Debug info') {
|
||||
const info = {
|
||||
repr: this.repr(),
|
||||
|
||||
@@ -6,6 +6,7 @@ import { SyncableMap } from 'common/util/SyncableMap/SyncableMap';
|
||||
import { CanvasCacheModule } from 'features/controlLayers/konva/CanvasCacheModule';
|
||||
import { CanvasCompositorModule } from 'features/controlLayers/konva/CanvasCompositorModule';
|
||||
import { CanvasFilterModule } from 'features/controlLayers/konva/CanvasFilterModule';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { CanvasRenderingModule } from 'features/controlLayers/konva/CanvasRenderingModule';
|
||||
import { CanvasStageModule } from 'features/controlLayers/konva/CanvasStageModule';
|
||||
import { CanvasWorkerModule } from 'features/controlLayers/konva/CanvasWorkerModule.js';
|
||||
@@ -19,20 +20,22 @@ import type { CanvasLayerAdapter } from './CanvasLayerAdapter';
|
||||
import type { CanvasMaskAdapter } from './CanvasMaskAdapter';
|
||||
import { CanvasPreviewModule } from './CanvasPreviewModule';
|
||||
import { CanvasStateApiModule } from './CanvasStateApiModule';
|
||||
import { setStageEventHandlers } from './events';
|
||||
|
||||
export const $canvasManager = atom<CanvasManager | null>(null);
|
||||
const TYPE = 'manager';
|
||||
|
||||
export class CanvasManager {
|
||||
readonly type = TYPE;
|
||||
export class CanvasManager extends CanvasModuleBase {
|
||||
readonly type = 'manager';
|
||||
|
||||
id: string;
|
||||
path: string[];
|
||||
manager: CanvasManager;
|
||||
log: Logger;
|
||||
|
||||
store: AppStore;
|
||||
socket: AppSocket;
|
||||
|
||||
subscriptions = new Set<() => void>();
|
||||
|
||||
adapters = {
|
||||
rasterLayers: new SyncableMap<string, CanvasLayerAdapter>(),
|
||||
controlLayers: new SyncableMap<string, CanvasLayerAdapter>(),
|
||||
@@ -61,8 +64,21 @@ export class CanvasManager {
|
||||
_isDebugging: boolean = false;
|
||||
|
||||
constructor(stage: Konva.Stage, container: HTMLDivElement, store: AppStore, socket: AppSocket) {
|
||||
super();
|
||||
this.id = getPrefixedId(this.type);
|
||||
this.path = [this.id];
|
||||
this.manager = this;
|
||||
this.log = logger('canvas').child((message) => {
|
||||
return {
|
||||
...message,
|
||||
context: {
|
||||
...this.getLoggingContext(),
|
||||
...message.context,
|
||||
},
|
||||
};
|
||||
});
|
||||
this.log.debug('Creating canvas manager module');
|
||||
|
||||
this.store = store;
|
||||
this.socket = socket;
|
||||
|
||||
@@ -81,16 +97,6 @@ export class CanvasManager {
|
||||
this.stage.addLayer(this.background.konva.layer);
|
||||
}
|
||||
|
||||
log = logger('canvas').child((message) => {
|
||||
return {
|
||||
...message,
|
||||
context: {
|
||||
...this.getLoggingContext(),
|
||||
...message.context,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
enableDebugging() {
|
||||
this._isDebugging = true;
|
||||
this.logDebugInfo();
|
||||
@@ -101,37 +107,61 @@ export class CanvasManager {
|
||||
}
|
||||
|
||||
initialize = () => {
|
||||
this.log.debug('Initializing canvas manager');
|
||||
this.log.debug('Initializing canvas manager module');
|
||||
|
||||
// These atoms require the canvas manager to be set up before we can provide their initial values
|
||||
this.stateApi.$transformingEntity.set(null);
|
||||
this.stateApi.$toolState.set(this.stateApi.getToolState());
|
||||
this.stateApi.$selectedEntityIdentifier.set(this.stateApi.getState().selectedEntityIdentifier);
|
||||
this.stateApi.$selectedEntityIdentifier.set(this.stateApi.getCanvasState().selectedEntityIdentifier);
|
||||
this.stateApi.$currentFill.set(this.stateApi.getCurrentFill());
|
||||
this.stateApi.$selectedEntity.set(this.stateApi.getSelectedEntity());
|
||||
|
||||
const cleanupEventHandlers = setStageEventHandlers(this);
|
||||
const cleanupStage = this.stage.initialize();
|
||||
const cleanupStore = this.store.subscribe(this.renderer.render);
|
||||
this.subscriptions.add(this.store.subscribe(this.renderer.render));
|
||||
this.stage.initialize();
|
||||
};
|
||||
|
||||
return () => {
|
||||
this.log.debug('Cleaning up canvas manager');
|
||||
for (const adapter of this.adapters.getAll()) {
|
||||
adapter.destroy();
|
||||
}
|
||||
this.background.destroy();
|
||||
this.preview.destroy();
|
||||
cleanupStore();
|
||||
cleanupEventHandlers();
|
||||
cleanupStage();
|
||||
};
|
||||
destroy = () => {
|
||||
this.log.debug('Destroying canvas manager module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
for (const adapter of this.adapters.getAll()) {
|
||||
adapter.destroy();
|
||||
}
|
||||
this.stateApi.destroy();
|
||||
this.preview.destroy();
|
||||
this.background.destroy();
|
||||
this.filter.destroy();
|
||||
this.worker.destroy();
|
||||
this.renderer.destroy();
|
||||
this.compositor.destroy();
|
||||
this.stage.destroy();
|
||||
$canvasManager.set(null);
|
||||
};
|
||||
|
||||
setCanvasManager = () => {
|
||||
this.log.debug('Setting canvas manager');
|
||||
this.log.debug('Setting canvas manager global');
|
||||
$canvasManager.set(this);
|
||||
};
|
||||
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
rasterLayers: Array.from(this.adapters.rasterLayers.values()).map((adapter) => adapter.repr()),
|
||||
controlLayers: Array.from(this.adapters.controlLayers.values()).map((adapter) => adapter.repr()),
|
||||
inpaintMasks: Array.from(this.adapters.inpaintMasks.values()).map((adapter) => adapter.repr()),
|
||||
regionMasks: Array.from(this.adapters.regionMasks.values()).map((adapter) => adapter.repr()),
|
||||
stateApi: this.stateApi.repr(),
|
||||
preview: this.preview.repr(),
|
||||
background: this.background.repr(),
|
||||
filter: this.filter.repr(),
|
||||
worker: this.worker.repr(),
|
||||
renderer: this.renderer.repr(),
|
||||
compositor: this.compositor.repr(),
|
||||
stage: this.stage.repr(),
|
||||
};
|
||||
};
|
||||
|
||||
getLoggingContext = (): SerializableObject => {
|
||||
return {
|
||||
path: this.path.join('.'),
|
||||
@@ -153,11 +183,6 @@ export class CanvasManager {
|
||||
logDebugInfo() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Canvas manager', this);
|
||||
for (const adapter of this.adapters.getAll()) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(adapter.id, adapter);
|
||||
}
|
||||
this.log.debug({ manager: this.repr() }, 'Canvas manager');
|
||||
}
|
||||
|
||||
getPrefixedId = getPrefixedId;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||
import { CanvasTransformer } from 'features/controlLayers/konva/CanvasTransformer';
|
||||
import { getLastPointOfLine } from 'features/controlLayers/konva/util';
|
||||
import type {
|
||||
CanvasBrushLineState,
|
||||
CanvasEntityIdentifier,
|
||||
CanvasEraserLineState,
|
||||
CanvasInpaintMaskState,
|
||||
CanvasRegionalGuidanceState,
|
||||
CanvasV2State,
|
||||
Coordinate,
|
||||
Rect,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { getEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
@@ -17,13 +21,14 @@ import { get, omit } from 'lodash-es';
|
||||
import type { Logger } from 'roarr';
|
||||
import stableHash from 'stable-hash';
|
||||
|
||||
export class CanvasMaskAdapter {
|
||||
export class CanvasMaskAdapter extends CanvasModuleBase {
|
||||
readonly type = 'mask_adapter';
|
||||
|
||||
id: string;
|
||||
path: string[];
|
||||
manager: CanvasManager;
|
||||
log: Logger;
|
||||
subscriptions = new Set<() => void>();
|
||||
|
||||
state: CanvasInpaintMaskState | CanvasRegionalGuidanceState;
|
||||
|
||||
@@ -37,11 +42,14 @@ export class CanvasMaskAdapter {
|
||||
};
|
||||
|
||||
constructor(state: CanvasMaskAdapter['state'], manager: CanvasMaskAdapter['manager']) {
|
||||
super();
|
||||
this.id = state.id;
|
||||
this.manager = manager;
|
||||
this.path = this.manager.path.concat(this.id);
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
this.log.debug({ state }, 'Creating mask');
|
||||
|
||||
this.log.debug({ state }, 'Creating mask adapter module');
|
||||
|
||||
this.state = state;
|
||||
|
||||
this.konva = {
|
||||
@@ -67,18 +75,14 @@ export class CanvasMaskAdapter {
|
||||
};
|
||||
|
||||
destroy = (): void => {
|
||||
this.log.debug('Destroying mask');
|
||||
// We need to call the destroy method on all children so they can do their own cleanup.
|
||||
this.log.debug('Destroying mask adapter module');
|
||||
|
||||
this.transformer.destroy();
|
||||
this.renderer.destroy();
|
||||
this.konva.layer.destroy();
|
||||
};
|
||||
|
||||
update = async (arg?: {
|
||||
state: CanvasMaskAdapter['state'];
|
||||
toolState: CanvasV2State['tool'];
|
||||
isSelected: boolean;
|
||||
}) => {
|
||||
update = async (arg?: { state: CanvasMaskAdapter['state'] }) => {
|
||||
const state = get(arg, 'state', this.state);
|
||||
|
||||
if (!this.isFirstRender && state === this.state && state.fill === this.state.fill) {
|
||||
@@ -136,10 +140,24 @@ export class CanvasMaskAdapter {
|
||||
this.konva.layer.visible(isEnabled);
|
||||
};
|
||||
|
||||
getLastPointOfLastLine = (type: CanvasBrushLineState['type'] | CanvasEraserLineState['type']): Coordinate | null => {
|
||||
const lastObject = this.state.objects[this.state.objects.length - 1];
|
||||
if (!lastObject) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (lastObject.type === type) {
|
||||
return getLastPointOfLine(lastObject.points);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
state: deepClone(this.state),
|
||||
};
|
||||
};
|
||||
@@ -165,7 +183,8 @@ export class CanvasMaskAdapter {
|
||||
const canvas = this.renderer.getCanvas(rect, attrs);
|
||||
return canvas;
|
||||
};
|
||||
getLoggingContext = (): SerializableObject => {
|
||||
|
||||
getLoggingContext = () => {
|
||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import type { Logger } from 'roarr';
|
||||
|
||||
export abstract class CanvasModuleBase {
|
||||
abstract id: string;
|
||||
abstract type: string;
|
||||
abstract path: string[];
|
||||
abstract manager: CanvasManager;
|
||||
abstract log: Logger;
|
||||
abstract subscriptions: Set<() => void>;
|
||||
|
||||
abstract getLoggingContext: () => SerializableObject;
|
||||
abstract destroy: () => void;
|
||||
abstract repr: () => SerializableObject & {
|
||||
id: string;
|
||||
path: string[];
|
||||
type: string;
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { CanvasBrushLineRenderer } from 'features/controlLayers/konva/CanvasBrushLine';
|
||||
import { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine';
|
||||
@@ -6,6 +5,7 @@ import { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage';
|
||||
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { CanvasRectRenderer } from 'features/controlLayers/konva/CanvasRect';
|
||||
import { LightnessToAlphaFilter } from 'features/controlLayers/konva/filters';
|
||||
import { getPatternSVG } from 'features/controlLayers/konva/patterns/getPatternSVG';
|
||||
@@ -30,6 +30,7 @@ import type { GroupConfig } from 'konva/lib/Group';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { atom } from 'nanostores';
|
||||
import type { Logger } from 'roarr';
|
||||
import { serializeError } from 'serialize-error';
|
||||
import { getImageDTO, uploadImage } from 'services/api/endpoints/images';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
@@ -55,8 +56,8 @@ type AnyObjectState = CanvasBrushLineState | CanvasEraserLineState | CanvasImage
|
||||
/**
|
||||
* Handles rendering of objects for a canvas entity.
|
||||
*/
|
||||
export class CanvasObjectRenderer {
|
||||
readonly type = 'object_renderer';
|
||||
export class CanvasObjectRenderer extends CanvasModuleBase {
|
||||
readonly type = 'entity_object_renderer';
|
||||
|
||||
id: string;
|
||||
path: string[];
|
||||
@@ -123,12 +124,13 @@ export class CanvasObjectRenderer {
|
||||
$canvasCache = atom<{ canvas: HTMLCanvasElement; rect: Rect } | null>(null);
|
||||
|
||||
constructor(parent: CanvasLayerAdapter | CanvasMaskAdapter) {
|
||||
super();
|
||||
this.id = getPrefixedId(this.type);
|
||||
this.parent = parent;
|
||||
this.path = this.parent.path.concat(this.id);
|
||||
this.manager = parent.manager;
|
||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||
this.log.trace('Creating object renderer');
|
||||
this.log.debug('Creating entity object renderer module');
|
||||
|
||||
this.konva = {
|
||||
objectGroup: new Konva.Group({ name: `${this.type}:object_group`, listening: false }),
|
||||
@@ -156,11 +158,12 @@ export class CanvasObjectRenderer {
|
||||
this.parent.konva.layer.add(this.konva.compositing.group);
|
||||
}
|
||||
|
||||
// When switching tool, commit the buffer. This is necessary to prevent the buffer from being lost when the
|
||||
// user switches tool mid-drawing, for example by pressing space to pan the stage. It's easy to press space
|
||||
// to pan _before_ releasing the mouse button, which would cause the buffer to be lost if we didn't commit it.
|
||||
this.subscriptions.add(
|
||||
this.manager.stateApi.$toolState.listen((newVal, oldVal) => {
|
||||
if (newVal.selected !== oldVal.selected) {
|
||||
this.commitBuffer();
|
||||
}
|
||||
this.manager.stateApi.$tool.listen(() => {
|
||||
this.commitBuffer();
|
||||
})
|
||||
);
|
||||
|
||||
@@ -548,17 +551,22 @@ export class CanvasObjectRenderer {
|
||||
if (this.parent.transformer.pixelRect.width === 0 || this.parent.transformer.pixelRect.height === 0) {
|
||||
return;
|
||||
}
|
||||
const canvas = this.konva.objectGroup._getCachedSceneCanvas()._canvas as HTMLCanvasElement | undefined | null;
|
||||
if (canvas) {
|
||||
const nodeRect = this.parent.transformer.nodeRect;
|
||||
const pixelRect = this.parent.transformer.pixelRect;
|
||||
const rect = {
|
||||
x: pixelRect.x - nodeRect.x,
|
||||
y: pixelRect.y - nodeRect.y,
|
||||
width: pixelRect.width,
|
||||
height: pixelRect.height,
|
||||
};
|
||||
this.$canvasCache.set({ rect, canvas });
|
||||
try {
|
||||
const canvas = this.konva.objectGroup._getCachedSceneCanvas()._canvas as HTMLCanvasElement | undefined | null;
|
||||
if (canvas) {
|
||||
const nodeRect = this.parent.transformer.nodeRect;
|
||||
const pixelRect = this.parent.transformer.pixelRect;
|
||||
const rect = {
|
||||
x: pixelRect.x - nodeRect.x,
|
||||
y: pixelRect.y - nodeRect.y,
|
||||
width: pixelRect.width,
|
||||
height: pixelRect.height,
|
||||
};
|
||||
this.$canvasCache.set({ rect, canvas });
|
||||
}
|
||||
} catch (error) {
|
||||
// We are using an internal Konva method, so we need to catch any errors that may occur.
|
||||
this.log.warn({ error: serializeError(error) }, 'Failed to update preview canvas');
|
||||
}
|
||||
}, 300);
|
||||
|
||||
@@ -596,11 +604,8 @@ export class CanvasObjectRenderer {
|
||||
* Destroys this renderer and all of its object renderers.
|
||||
*/
|
||||
destroy = () => {
|
||||
this.log.trace('Destroying object renderer');
|
||||
for (const cleanup of this.subscriptions) {
|
||||
this.log.trace('Cleaning up listener');
|
||||
cleanup();
|
||||
}
|
||||
this.log.debug('Destroying entity object renderer module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
for (const renderer of this.renderers.values()) {
|
||||
renderer.destroy();
|
||||
}
|
||||
@@ -615,13 +620,14 @@ export class CanvasObjectRenderer {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
parent: this.parent.id,
|
||||
renderers: Array.from(this.renderers.values()).map((renderer) => renderer.repr()),
|
||||
buffer: this.bufferRenderer?.repr(),
|
||||
};
|
||||
};
|
||||
|
||||
getLoggingContext = (): SerializableObject => {
|
||||
getLoggingContext = () => {
|
||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||
};
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user