diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json index ef1bbb04503..6b1269100f0 100644 --- a/invokeai/frontend/web/package.json +++ b/invokeai/frontend/web/package.json @@ -58,10 +58,11 @@ "@dagrejs/dagre": "^1.1.4", "@dagrejs/graphlib": "^2.2.4", "@fontsource-variable/inter": "^5.1.0", - "@invoke-ai/ui-library": "^0.0.44", + "@invoke-ai/ui-library": "^0.0.46", "@nanostores/react": "^0.7.3", "@reduxjs/toolkit": "2.2.3", "@roarr/browser-log-writer": "^1.3.0", + "@xyflow/react": "^12.4.2", "async-mutex": "^0.5.0", "chakra-react-select": "^4.9.2", "cmdk": "^1.0.0", @@ -96,9 +97,9 @@ "react-icons": "^5.3.0", "react-redux": "9.1.2", "react-resizable-panels": "^2.1.4", + "react-textarea-autosize": "^8.5.7", "react-use": "^17.5.1", "react-virtuoso": "^4.10.4", - "reactflow": "^11.11.4", "redux-dynamic-middlewares": "^2.2.0", "redux-remember": "^5.1.0", "redux-undo": "^1.1.0", diff --git a/invokeai/frontend/web/pnpm-lock.yaml b/invokeai/frontend/web/pnpm-lock.yaml index 4d7a0830d24..cf43a853458 100644 --- a/invokeai/frontend/web/pnpm-lock.yaml +++ b/invokeai/frontend/web/pnpm-lock.yaml @@ -24,8 +24,8 @@ dependencies: specifier: ^5.1.0 version: 5.1.0 '@invoke-ai/ui-library': - specifier: ^0.0.44 - version: 0.0.44(@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.1.0)(@types/react@18.3.11)(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1) + specifier: ^0.0.46 + version: 0.0.46(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.1.0)(@types/react@18.3.11)(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1) '@nanostores/react': specifier: ^0.7.3 version: 0.7.3(nanostores@0.11.3)(react@18.3.1) @@ -35,12 +35,15 @@ dependencies: '@roarr/browser-log-writer': specifier: ^1.3.0 version: 1.3.0 + '@xyflow/react': + specifier: ^12.4.2 + version: 12.4.2(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) async-mutex: specifier: ^0.5.0 version: 0.5.0 chakra-react-select: specifier: ^4.9.2 - version: 4.9.2(@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.11)(react-dom@18.3.1)(react@18.3.1) + version: 4.9.2(@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.14.0)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) cmdk: specifier: ^1.0.0 version: 1.0.0(@types/react-dom@18.3.0)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) @@ -137,15 +140,15 @@ dependencies: react-resizable-panels: specifier: ^2.1.4 version: 2.1.4(react-dom@18.3.1)(react@18.3.1) + react-textarea-autosize: + specifier: ^8.5.7 + version: 8.5.7(@types/react@18.3.11)(react@18.3.1) react-use: specifier: ^17.5.1 version: 17.5.1(react-dom@18.3.1)(react@18.3.1) react-virtuoso: specifier: ^4.10.4 version: 4.10.4(react-dom@18.3.1)(react@18.3.1) - reactflow: - specifier: ^11.11.4 - version: 11.11.4(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) redux-dynamic-middlewares: specifier: ^2.2.0 version: 2.2.0 @@ -572,7 +575,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.3)(@emotion/styled@11.13.0)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(react@18.3.1) react: 18.3.1 dev: false @@ -595,7 +598,7 @@ packages: react: '>=18' dependencies: '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(react@18.3.1) react: 18.3.1 dev: false @@ -605,7 +608,7 @@ packages: '@chakra-ui/react': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/react': 2.10.4(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/react': 2.10.4(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1) react: 18.3.1 dev: false @@ -621,7 +624,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.3)(@emotion/styled@11.13.0)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(react@18.3.1) react: 18.3.1 dev: false @@ -638,7 +641,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.3)(@emotion/styled@11.13.0)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(react@18.3.1) react: 18.3.1 dev: false @@ -663,7 +666,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.3)(@emotion/styled@11.13.0)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(react@18.3.1) '@chakra-ui/transition': 2.1.0(framer-motion@11.10.0)(react@18.3.1) framer-motion: 11.10.0(react-dom@18.3.1)(react@18.3.1) react: 18.3.1 @@ -828,7 +831,7 @@ packages: react: 18.3.1 dev: false - /@chakra-ui/react@2.10.4(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1): + /@chakra-ui/react@2.10.4(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-XyRWnuZ1Uw7Mlj5pKUGO5/WhnIHP/EOrpy6lGZC1yWlkd0eIfIpYMZ1ALTZx4KPEdbBaes48dgiMT2ROCqLhkA==} peerDependencies: '@emotion/react': '>=11' @@ -841,8 +844,8 @@ packages: '@chakra-ui/styled-system': 2.12.1(react@18.3.1) '@chakra-ui/theme': 3.4.7(@chakra-ui/styled-system@2.12.1)(react@18.3.1) '@chakra-ui/utils': 2.2.3(react@18.3.1) - '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.11)(react@18.3.1) + '@emotion/react': 11.14.0(@types/react@18.3.11)(react@18.3.1) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0)(@types/react@18.3.11)(react@18.3.1) '@popperjs/core': 2.11.8 '@zag-js/focus-visible': 0.31.1 aria-hidden: 1.2.4 @@ -867,7 +870,7 @@ packages: react: '>=18' dependencies: '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(react@18.3.1) react: 18.3.1 dev: false @@ -888,7 +891,7 @@ packages: lodash.mergewith: 4.6.2 dev: false - /@chakra-ui/system@2.6.2(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1): + /@chakra-ui/system@2.6.2(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(react@18.3.1): resolution: {integrity: sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==} peerDependencies: '@emotion/react': ^11.0.0 @@ -901,8 +904,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.3(@types/react@18.3.11)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.11)(react@18.3.1) + '@emotion/react': 11.14.0(@types/react@18.3.11)(react@18.3.1) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0)(@types/react@18.3.11)(react@18.3.1) react: 18.3.1 react-fast-compare: 3.2.2 dev: false @@ -1023,6 +1026,24 @@ packages: - supports-color dev: false + /@emotion/babel-plugin@11.13.5: + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + dependencies: + '@babel/helper-module-imports': 7.25.7 + '@babel/runtime': 7.25.7 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + dev: false + /@emotion/cache@11.13.1: resolution: {integrity: sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==} dependencies: @@ -1033,6 +1054,16 @@ packages: stylis: 4.2.0 dev: false + /@emotion/cache@11.14.0: + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + dev: false + /@emotion/hash@0.9.2: resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} dev: false @@ -1084,6 +1115,29 @@ packages: - supports-color dev: false + /@emotion/react@11.14.0(@types/react@18.3.11)(react@18.3.1): + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.25.7 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + '@types/react': 18.3.11 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + transitivePeerDependencies: + - supports-color + dev: false + /@emotion/serialize@1.3.2: resolution: {integrity: sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==} dependencies: @@ -1094,12 +1148,22 @@ packages: csstype: 3.1.3 dev: false + /@emotion/serialize@1.3.3: + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + dev: false + /@emotion/sheet@1.4.0: resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} dev: false - /@emotion/styled@11.13.0(@emotion/react@11.13.3)(@types/react@18.3.11)(react@18.3.1): - resolution: {integrity: sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==} + /@emotion/styled@11.14.0(@emotion/react@11.14.0)(@types/react@18.3.11)(react@18.3.1): + resolution: {integrity: sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==} peerDependencies: '@emotion/react': ^11.0.0-rc.0 '@types/react': '*' @@ -1109,12 +1173,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.25.7 - '@emotion/babel-plugin': 11.12.0 + '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.3.1 - '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1) - '@emotion/serialize': 1.3.2 - '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) - '@emotion/utils': 1.4.1 + '@emotion/react': 11.14.0(@types/react@18.3.11)(react@18.3.1) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 '@types/react': 18.3.11 react: 18.3.1 transitivePeerDependencies: @@ -1133,10 +1197,22 @@ packages: react: 18.3.1 dev: false + /@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1): + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.3.1 + dev: false + /@emotion/utils@1.4.1: resolution: {integrity: sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==} dev: false + /@emotion/utils@1.4.2: + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + dev: false + /@emotion/weak-memoize@0.4.0: resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} dev: false @@ -1678,25 +1754,25 @@ packages: prettier: 3.3.3 dev: true - /@invoke-ai/ui-library@0.0.44(@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.1.0)(@types/react@18.3.11)(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-PDseHmdr8oi8cmrpx3UwIYHn4NduAJX2R0pM0pyM54xrCMPMgYiCbC/eOs8Gt4fBc2ziiPZ9UGoW4evnE3YJsg==} + /@invoke-ai/ui-library@0.0.46(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.1.0)(@types/react@18.3.11)(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-3YBuWWhRbTUHi0RZKeyvDEvweoyZmeBdUGJIhemjdAgGx6l98rAMeCs8IQH+SYjSAIhiGRGf45fQ33PDK8Jkmw==} peerDependencies: '@fontsource-variable/inter': ^5.0.16 react: ^18.2.0 react-dom: ^18.2.0 dependencies: - '@chakra-ui/anatomy': 2.2.2 + '@chakra-ui/anatomy': 2.3.5 '@chakra-ui/icons': 2.2.4(@chakra-ui/react@2.10.4)(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.10.4(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.11)(framer-motion@11.10.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.3(@types/react@18.3.11)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.11)(react@18.3.1) + '@chakra-ui/react': 2.10.4(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/styled-system': 2.12.1(react@18.3.1) + '@chakra-ui/theme-tools': 2.2.7(@chakra-ui/styled-system@2.12.1)(react@18.3.1) + '@emotion/react': 11.14.0(@types/react@18.3.11)(react@18.3.1) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0)(@types/react@18.3.11)(react@18.3.1) '@fontsource-variable/inter': 5.1.0 '@nanostores/react': 0.7.3(nanostores@0.11.3)(react@18.3.1) - chakra-react-select: 4.9.2(@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.11)(react-dom@18.3.1)(react@18.3.1) + chakra-react-select: 4.10.1(@chakra-ui/react@2.10.4)(@emotion/react@11.14.0)(@types/react@18.3.11)(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.3 @@ -1704,15 +1780,10 @@ packages: overlayscrollbars-react: 0.5.6(overlayscrollbars@2.10.0)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-i18next: 15.0.2(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1) - react-icons: 5.3.0(react@18.3.1) - react-select: 5.8.0(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) + react-i18next: 15.4.0(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1) + react-icons: 5.4.0(react@18.3.1) + react-select: 5.10.0(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) transitivePeerDependencies: - - '@chakra-ui/form-control' - - '@chakra-ui/icon' - - '@chakra-ui/media-query' - - '@chakra-ui/menu' - - '@chakra-ui/spinner' - '@chakra-ui/system' - '@types/react' - i18next @@ -2170,114 +2241,6 @@ packages: react: 18.3.1 dev: false - /@reactflow/background@11.3.14(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.11.4(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - classcat: 5.0.5 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - zustand: 4.5.5(@types/react@18.3.11)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/controls@11.2.14(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.11.4(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - classcat: 5.0.5 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - zustand: 4.5.5(@types/react@18.3.11)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/core@11.11.4(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@types/d3': 7.4.3 - '@types/d3-drag': 3.0.7 - '@types/d3-selection': 3.0.10 - '@types/d3-zoom': 3.0.8 - classcat: 5.0.5 - d3-drag: 3.0.0 - d3-selection: 3.0.0 - d3-zoom: 3.0.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - zustand: 4.5.5(@types/react@18.3.11)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/minimap@11.7.14(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.11.4(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - '@types/d3-selection': 3.0.10 - '@types/d3-zoom': 3.0.8 - classcat: 5.0.5 - d3-selection: 3.0.0 - d3-zoom: 3.0.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - zustand: 4.5.5(@types/react@18.3.11)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/node-resizer@2.2.14(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.11.4(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - classcat: 5.0.5 - d3-drag: 3.0.0 - d3-selection: 3.0.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - zustand: 4.5.5(@types/react@18.3.11)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/node-toolbar@1.3.14(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.11.4(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - classcat: 5.0.5 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - zustand: 4.5.5(@types/react@18.3.11)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - /@redocly/ajv@8.11.2: resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} dependencies: @@ -3215,137 +3178,26 @@ packages: '@types/node': 20.16.10 dev: true - /@types/d3-array@3.2.1: - resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} - dev: false - - /@types/d3-axis@3.0.6: - resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} - dependencies: - '@types/d3-selection': 3.0.10 - dev: false - - /@types/d3-brush@3.0.6: - resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} - dependencies: - '@types/d3-selection': 3.0.10 - dev: false - - /@types/d3-chord@3.0.6: - resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} - dev: false - /@types/d3-color@3.1.3: resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} dev: false - /@types/d3-contour@3.0.6: - resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} - dependencies: - '@types/d3-array': 3.2.1 - '@types/geojson': 7946.0.14 - dev: false - - /@types/d3-delaunay@6.0.4: - resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} - dev: false - - /@types/d3-dispatch@3.0.6: - resolution: {integrity: sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==} - dev: false - /@types/d3-drag@3.0.7: resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} dependencies: '@types/d3-selection': 3.0.10 dev: false - /@types/d3-dsv@3.0.7: - resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} - dev: false - - /@types/d3-ease@3.0.2: - resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} - dev: false - - /@types/d3-fetch@3.0.7: - resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} - dependencies: - '@types/d3-dsv': 3.0.7 - dev: false - - /@types/d3-force@3.0.10: - resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} - dev: false - - /@types/d3-format@3.0.4: - resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} - dev: false - - /@types/d3-geo@3.1.0: - resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} - dependencies: - '@types/geojson': 7946.0.14 - dev: false - - /@types/d3-hierarchy@3.1.7: - resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} - dev: false - /@types/d3-interpolate@3.0.4: resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} dependencies: '@types/d3-color': 3.1.3 dev: false - /@types/d3-path@3.1.0: - resolution: {integrity: sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==} - dev: false - - /@types/d3-polygon@3.0.2: - resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} - dev: false - - /@types/d3-quadtree@3.0.6: - resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} - dev: false - - /@types/d3-random@3.0.3: - resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} - dev: false - - /@types/d3-scale-chromatic@3.0.3: - resolution: {integrity: sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==} - dev: false - - /@types/d3-scale@4.0.8: - resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==} - dependencies: - '@types/d3-time': 3.0.3 - dev: false - /@types/d3-selection@3.0.10: resolution: {integrity: sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==} dev: false - /@types/d3-shape@3.1.6: - resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==} - dependencies: - '@types/d3-path': 3.1.0 - dev: false - - /@types/d3-time-format@4.0.3: - resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} - dev: false - - /@types/d3-time@3.0.3: - resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==} - dev: false - - /@types/d3-timer@3.0.2: - resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - dev: false - /@types/d3-transition@3.0.8: resolution: {integrity: sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==} dependencies: @@ -3359,41 +3211,6 @@ packages: '@types/d3-selection': 3.0.10 dev: false - /@types/d3@7.4.3: - resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} - dependencies: - '@types/d3-array': 3.2.1 - '@types/d3-axis': 3.0.6 - '@types/d3-brush': 3.0.6 - '@types/d3-chord': 3.0.6 - '@types/d3-color': 3.1.3 - '@types/d3-contour': 3.0.6 - '@types/d3-delaunay': 6.0.4 - '@types/d3-dispatch': 3.0.6 - '@types/d3-drag': 3.0.7 - '@types/d3-dsv': 3.0.7 - '@types/d3-ease': 3.0.2 - '@types/d3-fetch': 3.0.7 - '@types/d3-force': 3.0.10 - '@types/d3-format': 3.0.4 - '@types/d3-geo': 3.1.0 - '@types/d3-hierarchy': 3.1.7 - '@types/d3-interpolate': 3.0.4 - '@types/d3-path': 3.1.0 - '@types/d3-polygon': 3.0.2 - '@types/d3-quadtree': 3.0.6 - '@types/d3-random': 3.0.3 - '@types/d3-scale': 4.0.8 - '@types/d3-scale-chromatic': 3.0.3 - '@types/d3-selection': 3.0.10 - '@types/d3-shape': 3.1.6 - '@types/d3-time': 3.0.3 - '@types/d3-time-format': 4.0.3 - '@types/d3-timer': 3.0.2 - '@types/d3-transition': 3.0.8 - '@types/d3-zoom': 3.0.8 - dev: false - /@types/dateformat@5.0.2: resolution: {integrity: sha512-M95hNBMa/hnwErH+a+VOD/sYgTmo15OTYTM2Hr52/e0OdOuY+Crag+kd3/ioZrhg0WGbl9Sm3hR7UU+MH6rfOw==} dev: true @@ -3447,10 +3264,6 @@ packages: resolution: {integrity: sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==} dev: true - /@types/geojson@7946.0.14: - resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==} - dev: false - /@types/glob@7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: @@ -3985,6 +3798,34 @@ packages: resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} dev: false + /@xyflow/react@12.4.2(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-AFJKVc/fCPtgSOnRst3xdYJwiEcUN9lDY7EO/YiRvFHYCJGgfzg+jpvZjkTOnBLGyrMJre9378pRxAc3fsR06A==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@xyflow/system': 0.0.50 + classcat: 5.0.5 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.5(@types/react@18.3.11)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + + /@xyflow/system@0.0.50: + resolution: {integrity: sha512-HVUZd4LlY88XAaldFh2nwVxDOcdIBxGpQ5txzwfJPf+CAjj2BfYug1fHs2p4yS7YO8H6A3EFJQovBE8YuHkAdg==} + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-selection': 3.0.10 + '@types/d3-transition': 3.0.8 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + dev: false + /@zag-js/dom-query@0.31.1: resolution: {integrity: sha512-oiuohEXAXhBxpzzNm9k2VHGEOLC1SXlXSbRPcfBZ9so5NRQUA++zCE7cyQJqGLTZR0t3itFLlZqDbYEXRrefwg==} dev: false @@ -4426,7 +4267,25 @@ packages: pathval: 2.0.0 dev: true - /chakra-react-select@4.9.2(@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.11)(react-dom@18.3.1)(react@18.3.1): + /chakra-react-select@4.10.1(@chakra-ui/react@2.10.4)(@emotion/react@11.14.0)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-0d7lubrmcm7molVYNYWEYi7o71W8wn/WruINon+m23XQLYvJ+bZlYVawDdWYdJjX8O1nzJlTDo4b7CB6zTsr4A==} + peerDependencies: + '@chakra-ui/react': 2.x + '@emotion/react': ^11.8.1 + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + '@chakra-ui/react': 2.10.4(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1) + '@emotion/react': 11.14.0(@types/react@18.3.11)(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.11)(react-dom@18.3.1)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - supports-color + dev: false + + /chakra-react-select@4.9.2(@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.14.0)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-uhvKAJ1I2lbIwdn+wx0YvxX5rtQVI0gXL0apx0CXm3blIxk7qf6YuCh2TnGuGKst8gj8jUFZyhYZiGlcvgbBRQ==} peerDependencies: '@chakra-ui/form-control': ^2.0.0 @@ -4446,8 +4305,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.10.0)(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.3)(@emotion/styled@11.13.0)(react@18.3.1) - '@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.14.0)(@emotion/styled@11.14.0)(react@18.3.1) + '@emotion/react': 11.14.0(@types/react@18.3.11)(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.11)(react-dom@18.3.1)(react@18.3.1) @@ -7731,6 +7590,26 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /react-i18next@15.4.0(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-Py6UkX3zV08RTvL6ZANRoBh9sL/ne6rQq79XlkHEdd82cZr2H9usbWpUNVadJntIZP2pu3M2rL1CN+5rQYfYFw==} + peerDependencies: + i18next: '>= 23.2.3' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@babel/runtime': 7.25.7 + html-parse-stringify: 3.0.1 + i18next: 23.15.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /react-icons@5.3.0(react@18.3.1): resolution: {integrity: sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==} peerDependencies: @@ -7739,6 +7618,14 @@ packages: react: 18.3.1 dev: false + /react-icons@5.4.0(react@18.3.1): + resolution: {integrity: sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ==} + peerDependencies: + react: '*' + dependencies: + react: 18.3.1 + dev: false + /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -7837,6 +7724,28 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /react-select@5.10.0(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-k96gw+i6N3ExgDwPIg0lUPmexl1ygPe6u5BdQFNBhkpbwroIgCNXdubtIzHfThYXYYTubwOBafoMnn7ruEP1xA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + dependencies: + '@babel/runtime': 7.25.7 + '@emotion/cache': 11.14.0 + '@emotion/react': 11.14.0(@types/react@18.3.11)(react@18.3.1) + '@floating-ui/dom': 1.6.11 + '@types/react-transition-group': 4.4.11 + memoize-one: 6.0.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1) + use-isomorphic-layout-effect: 1.2.0(@types/react@18.3.11)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - supports-color + dev: false + /react-select@5.8.0(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==} peerDependencies: @@ -7876,6 +7785,20 @@ packages: tslib: 2.7.0 dev: false + /react-textarea-autosize@8.5.7(@types/react@18.3.11)(react@18.3.1): + resolution: {integrity: sha512-2MqJ3p0Jh69yt9ktFIaZmORHXw4c4bxSIhCeWiFwmJ9EYKgLmuNII3e9c9b2UO+ijl4StnpZdqpxNIhTdHvqtQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + dependencies: + '@babel/runtime': 7.25.7 + react: 18.3.1 + use-composed-ref: 1.4.0(@types/react@18.3.11)(react@18.3.1) + use-latest: 1.3.0(@types/react@18.3.11)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + dev: false + /react-transition-group@4.4.5(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -7941,25 +7864,6 @@ packages: dependencies: loose-envify: 1.4.0 - /reactflow@11.11.4(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/background': 11.3.14(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - '@reactflow/controls': 11.2.14(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - '@reactflow/core': 11.11.4(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - '@reactflow/minimap': 11.7.14(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - '@reactflow/node-resizer': 2.2.14(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - '@reactflow/node-toolbar': 1.3.14(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -9072,6 +8976,19 @@ packages: tslib: 2.7.0 dev: false + /use-composed-ref@1.4.0(@types/react@18.3.11)(react@18.3.1): + resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.11 + react: 18.3.1 + dev: false + /use-debounce@10.0.3(react@18.3.1): resolution: {integrity: sha512-DxQSI9ZKso689WM1mjgGU3ozcxU1TJElBJ3X6S4SMzMNcm2lVH0AHmyXB+K7ewjz2BSUKJTDqTcwtSMRfB89dg==} engines: {node: '>= 16.0.0'} @@ -9102,6 +9019,33 @@ packages: react: 18.3.1 dev: false + /use-isomorphic-layout-effect@1.2.0(@types/react@18.3.11)(react@18.3.1): + resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.11 + react: 18.3.1 + dev: false + + /use-latest@1.3.0(@types/react@18.3.11)(react@18.3.1): + resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.11 + react: 18.3.1 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.3.11)(react@18.3.1) + dev: false + /use-sidecar@1.1.2(@types/react@18.3.11)(react@18.3.1): resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} diff --git a/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx b/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx index 325db314d56..1cdddb76a57 100644 --- a/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx +++ b/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx @@ -1,5 +1,6 @@ import '@fontsource-variable/inter'; import 'overlayscrollbars/overlayscrollbars.css'; +import '@xyflow/react/dist/base.css'; import { ChakraProvider, DarkMode, extendTheme, theme as _theme, TOAST_OPTIONS } from '@invoke-ai/ui-library'; import type { ReactNode } from 'react'; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts index 8d39448ad20..cde399e666a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts @@ -1,6 +1,6 @@ import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; -import { $nodeExecutionStates } from 'features/nodes/hooks/useExecutionState'; +import { $nodeExecutionStates } from 'features/nodes/hooks/useNodeExecutionState'; import { workflowLoaded, workflowLoadRequested } from 'features/nodes/store/actions'; import { $templates } from 'features/nodes/store/nodesSlice'; import { $needsFit } from 'features/nodes/store/reactFlowInstance'; diff --git a/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx b/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx index f9e4e11f085..756ffec0d91 100644 --- a/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx +++ b/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx @@ -1,5 +1,6 @@ -import type { As, ChakraProps, FlexProps } from '@invoke-ai/ui-library'; +import type { ChakraProps, FlexProps } from '@invoke-ai/ui-library'; import { Flex, Icon, Skeleton, Spinner, Text } from '@invoke-ai/ui-library'; +import type { ElementType } from 'react'; import { memo, useMemo } from 'react'; import { PiImageBold } from 'react-icons/pi'; import type { ImageDTO } from 'services/api/types'; @@ -28,7 +29,7 @@ IAILoadingImageFallback.displayName = 'IAILoadingImageFallback'; type IAINoImageFallbackProps = FlexProps & { label?: string; - icon?: As | null; + icon?: ElementType | null; boxSize?: ChakraProps['boxSize']; }; diff --git a/invokeai/frontend/web/src/common/components/NodeSelectionOverlay.tsx b/invokeai/frontend/web/src/common/components/NodeSelectionOverlay.tsx deleted file mode 100644 index 31ea415c909..00000000000 --- a/invokeai/frontend/web/src/common/components/NodeSelectionOverlay.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Box } from '@invoke-ai/ui-library'; -import { memo, useMemo } from 'react'; - -type Props = { - isSelected: boolean; - isHovered: boolean; -}; -const SelectionOverlay = ({ isSelected, isHovered }: Props) => { - const shadow = useMemo(() => { - if (isSelected && isHovered) { - return 'nodeHoveredSelected'; - } - if (isSelected) { - return 'nodeSelected'; - } - if (isHovered) { - return 'nodeHovered'; - } - return undefined; - }, [isHovered, isSelected]); - return ( - - ); -}; - -export default memo(SelectionOverlay); diff --git a/invokeai/frontend/web/src/common/hooks/useChakraThemeTokens.ts b/invokeai/frontend/web/src/common/hooks/useChakraThemeTokens.ts deleted file mode 100644 index 93345f6a4cb..00000000000 --- a/invokeai/frontend/web/src/common/hooks/useChakraThemeTokens.ts +++ /dev/null @@ -1,238 +0,0 @@ -import { useToken } from '@invoke-ai/ui-library'; - -export const useChakraThemeTokens = () => { - const [ - base50, - base100, - base150, - base200, - base250, - base300, - base350, - base400, - base450, - base500, - base550, - base600, - base650, - base700, - base750, - base800, - base850, - base900, - base950, - accent50, - accent100, - accent150, - accent200, - accent250, - accent300, - accent350, - accent400, - accent450, - accent500, - accent550, - accent600, - accent650, - accent700, - accent750, - accent800, - accent850, - accent900, - accent950, - baseAlpha50, - baseAlpha100, - baseAlpha150, - baseAlpha200, - baseAlpha250, - baseAlpha300, - baseAlpha350, - baseAlpha400, - baseAlpha450, - baseAlpha500, - baseAlpha550, - baseAlpha600, - baseAlpha650, - baseAlpha700, - baseAlpha750, - baseAlpha800, - baseAlpha850, - baseAlpha900, - baseAlpha950, - accentAlpha50, - accentAlpha100, - accentAlpha150, - accentAlpha200, - accentAlpha250, - accentAlpha300, - accentAlpha350, - accentAlpha400, - accentAlpha450, - accentAlpha500, - accentAlpha550, - accentAlpha600, - accentAlpha650, - accentAlpha700, - accentAlpha750, - accentAlpha800, - accentAlpha850, - accentAlpha900, - accentAlpha950, - ] = useToken('colors', [ - 'base.50', - 'base.100', - 'base.150', - 'base.200', - 'base.250', - 'base.300', - 'base.350', - 'base.400', - 'base.450', - 'base.500', - 'base.550', - 'base.600', - 'base.650', - 'base.700', - 'base.750', - 'base.800', - 'base.850', - 'base.900', - 'base.950', - 'accent.50', - 'accent.100', - 'accent.150', - 'accent.200', - 'accent.250', - 'accent.300', - 'accent.350', - 'accent.400', - 'accent.450', - 'accent.500', - 'accent.550', - 'accent.600', - 'accent.650', - 'accent.700', - 'accent.750', - 'accent.800', - 'accent.850', - 'accent.900', - 'accent.950', - 'baseAlpha.50', - 'baseAlpha.100', - 'baseAlpha.150', - 'baseAlpha.200', - 'baseAlpha.250', - 'baseAlpha.300', - 'baseAlpha.350', - 'baseAlpha.400', - 'baseAlpha.450', - 'baseAlpha.500', - 'baseAlpha.550', - 'baseAlpha.600', - 'baseAlpha.650', - 'baseAlpha.700', - 'baseAlpha.750', - 'baseAlpha.800', - 'baseAlpha.850', - 'baseAlpha.900', - 'baseAlpha.950', - 'accentAlpha.50', - 'accentAlpha.100', - 'accentAlpha.150', - 'accentAlpha.200', - 'accentAlpha.250', - 'accentAlpha.300', - 'accentAlpha.350', - 'accentAlpha.400', - 'accentAlpha.450', - 'accentAlpha.500', - 'accentAlpha.550', - 'accentAlpha.600', - 'accentAlpha.650', - 'accentAlpha.700', - 'accentAlpha.750', - 'accentAlpha.800', - 'accentAlpha.850', - 'accentAlpha.900', - 'accentAlpha.950', - ]); - - return { - base50, - base100, - base150, - base200, - base250, - base300, - base350, - base400, - base450, - base500, - base550, - base600, - base650, - base700, - base750, - base800, - base850, - base900, - base950, - accent50, - accent100, - accent150, - accent200, - accent250, - accent300, - accent350, - accent400, - accent450, - accent500, - accent550, - accent600, - accent650, - accent700, - accent750, - accent800, - accent850, - accent900, - accent950, - baseAlpha50, - baseAlpha100, - baseAlpha150, - baseAlpha200, - baseAlpha250, - baseAlpha300, - baseAlpha350, - baseAlpha400, - baseAlpha450, - baseAlpha500, - baseAlpha550, - baseAlpha600, - baseAlpha650, - baseAlpha700, - baseAlpha750, - baseAlpha800, - baseAlpha850, - baseAlpha900, - baseAlpha950, - accentAlpha50, - accentAlpha100, - accentAlpha150, - accentAlpha200, - accentAlpha250, - accentAlpha300, - accentAlpha350, - accentAlpha400, - accentAlpha450, - accentAlpha500, - accentAlpha550, - accentAlpha600, - accentAlpha650, - accentAlpha700, - accentAlpha750, - accentAlpha800, - accentAlpha850, - accentAlpha900, - accentAlpha950, - }; -}; diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/util.ts b/invokeai/frontend/web/src/features/controlLayers/konva/util.ts index f5a30afcc7e..b774b7ba8be 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/util.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/util.ts @@ -484,9 +484,10 @@ export function loadImage(src: string): Promise { export const nanoid = customAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 10); export function getPrefixedId( - prefix: CanvasEntityIdentifier['type'] | CanvasObjectState['type'] | (string & Record) + prefix: CanvasEntityIdentifier['type'] | CanvasObjectState['type'] | (string & Record), + separator = ':' ): string { - return `${prefix}:${nanoid()}`; + return `${prefix}${separator}${nanoid()}`; } export const getEmptyRect = (): Rect => { diff --git a/invokeai/frontend/web/src/features/dnd/DndListDropIndicator.tsx b/invokeai/frontend/web/src/features/dnd/DndListDropIndicator.tsx index 3a7152c0f73..600f0d51746 100644 --- a/invokeai/frontend/web/src/features/dnd/DndListDropIndicator.tsx +++ b/invokeai/frontend/web/src/features/dnd/DndListDropIndicator.tsx @@ -9,7 +9,8 @@ import type { DndListTargetState } from 'features/dnd/types'; */ const line = { thickness: 2, - backgroundColor: 'base.500', + backgroundColor: 'red', + // backgroundColor: 'base.500', }; type DropIndicatorProps = { @@ -104,7 +105,7 @@ function DndDropIndicatorInternal({ edge, gap = '0px' }: DropIndicatorProps) { ); } -export const DndListDropIndicator = ({ dndState }: { dndState: DndListTargetState }) => { +export const DndListDropIndicator = ({ dndState, gap }: { dndState: DndListTargetState; gap?: string }) => { if (dndState.type !== 'is-dragging-over') { return null; } @@ -117,7 +118,7 @@ export const DndListDropIndicator = ({ dndState }: { dndState: DndListTargetStat ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx b/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx index acc2fde7536..de0ab48d7c7 100644 --- a/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx @@ -1,5 +1,3 @@ -import 'reactflow/dist/style.css'; - import { Flex } from '@invoke-ai/ui-library'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import { useFocusRegion } from 'common/hooks/focus'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk.tsx index 90a1d7427b2..d97408c624c 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/AddNodeCmdk/AddNodeCmdk.tsx @@ -12,6 +12,7 @@ import { Text, } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import type { EdgeChange, NodeChange } from '@xyflow/react'; import { useAppSelector, useAppStore } from 'app/store/storeHooks'; import { CommandEmpty, CommandItem, CommandList, CommandRoot } from 'cmdk'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; @@ -31,6 +32,7 @@ import { findUnoccupiedPosition } from 'features/nodes/store/util/findUnoccupied import { getFirstValidConnection } from 'features/nodes/store/util/getFirstValidConnection'; import { connectionToEdge } from 'features/nodes/store/util/reactFlowUtil'; import { validateConnectionTypes } from 'features/nodes/store/util/validateConnectionTypes'; +import type { AnyEdge, AnyNode } from 'features/nodes/types/invocation'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; import { toast } from 'features/toast/toast'; @@ -41,8 +43,8 @@ import type { ChangeEvent } from 'react'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { PiCircuitryBold, PiFlaskBold, PiHammerBold, PiLightningFill } from 'react-icons/pi'; -import type { EdgeChange, NodeChange } from 'reactflow'; import type { S } from 'services/api/types'; +import { objectEntries } from 'tsafe'; const useThrottle = (value: T, limit: number) => { const [throttledValue, setThrottledValue] = useState(value); @@ -95,8 +97,8 @@ const useAddNode = () => { node.selected = true; // Deselect all other nodes and edges - const nodeChanges: NodeChange[] = [{ type: 'add', item: node }]; - const edgeChanges: EdgeChange[] = []; + const nodeChanges: NodeChange[] = [{ type: 'add', item: node }]; + const edgeChanges: EdgeChange[] = []; nodes.forEach(({ id, selected }) => { if (selected) { nodeChanges.push({ type: 'select', id, selected: false }); @@ -381,11 +383,11 @@ const NodeCommandList = memo(({ searchTerm, onSelect }: { searchTerm: string; on if (filter(template, searchTerm)) { const candidateFields = pendingConnection.handleType === 'source' ? template.inputs : template.outputs; - for (const field of Object.values(candidateFields)) { + for (const [_fieldName, fieldTemplate] of objectEntries(candidateFields)) { const sourceType = - pendingConnection.handleType === 'source' ? field.type : pendingConnection.fieldTemplate.type; + pendingConnection.handleType === 'source' ? pendingConnection.fieldTemplate.type : fieldTemplate.type; const targetType = - pendingConnection.handleType === 'target' ? field.type : pendingConnection.fieldTemplate.type; + pendingConnection.handleType === 'target' ? pendingConnection.fieldTemplate.type : fieldTemplate.type; if (validateConnectionTypes(sourceType, targetType)) { _items.push({ diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx index aaeea376a19..93093bbcf7d 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx @@ -1,11 +1,25 @@ import { useGlobalMenuClose, useToken } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import type { + EdgeChange, + HandleType, + NodeChange, + OnEdgesChange, + OnInit, + OnMoveEnd, + OnNodesChange, + OnReconnect, + ProOptions, + ReactFlowProps, + ReactFlowState, +} from '@xyflow/react'; +import { Background, ReactFlow, useStore as useReactFlowStore, useUpdateNodeInternals } from '@xyflow/react'; import { useAppDispatch, useAppSelector, useAppStore } from 'app/store/storeHooks'; import { useFocusRegion, useIsRegionFocused } from 'common/hooks/focus'; import { useConnection } from 'features/nodes/hooks/useConnection'; -import { useCopyPaste } from 'features/nodes/hooks/useCopyPaste'; -import { useSyncExecutionState } from 'features/nodes/hooks/useExecutionState'; import { useIsValidConnection } from 'features/nodes/hooks/useIsValidConnection'; +import { useNodeCopyPaste } from 'features/nodes/hooks/useNodeCopyPaste'; +import { useSyncExecutionState } from 'features/nodes/hooks/useNodeExecutionState'; import { useWorkflowWatcher } from 'features/nodes/hooks/useWorkflowWatcher'; import { $addNodeCmdk, @@ -30,23 +44,11 @@ import { } from 'features/nodes/store/selectors'; import { connectionToEdge } from 'features/nodes/store/util/reactFlowUtil'; import { selectSelectionMode, selectShouldSnapToGrid } from 'features/nodes/store/workflowSettingsSlice'; +import type { AnyEdge, AnyNode } from 'features/nodes/types/invocation'; import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; import type { CSSProperties, MouseEvent } from 'react'; import { memo, useCallback, useMemo, useRef } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; -import type { - EdgeChange, - NodeChange, - OnEdgesChange, - OnEdgeUpdateFunc, - OnInit, - OnMoveEnd, - OnNodesChange, - ProOptions, - ReactFlowProps, - ReactFlowState, -} from 'reactflow'; -import { Background, ReactFlow, useStore as useReactFlowStore, useUpdateNodeInternals } from 'reactflow'; import CustomConnectionLine from './connectionLines/CustomConnectionLine'; import InvocationCollapsedEdge from './edges/InvocationCollapsedEdge'; @@ -58,13 +60,13 @@ import NotesNode from './nodes/Notes/NotesNode'; const edgeTypes = { collapsed: InvocationCollapsedEdge, default: InvocationDefaultEdge, -}; +} as const; const nodeTypes = { invocation: InvocationNodeWrapper, current_image: CurrentImageNode, notes: NotesNode, -}; +} as const; // TODO: can we support reactflow? if not, we could style the attribution so it matches the app const proOptions: ProOptions = { hideAttribution: true }; @@ -97,7 +99,7 @@ export const Flow = memo(() => { const [borderRadius] = useToken('radii', ['base']); const flowStyles = useMemo(() => ({ borderRadius }), [borderRadius]); - const onNodesChange: OnNodesChange = useCallback( + const onNodesChange: OnNodesChange = useCallback( (nodeChanges) => { dispatch(nodesChanged(nodeChanges)); const flow = $flow.get(); @@ -112,7 +114,7 @@ export const Flow = memo(() => { [dispatch, needsFit] ); - const onEdgesChange: OnEdgesChange = useCallback( + const onEdgesChange: OnEdgesChange = useCallback( (changes) => { if (changes.length > 0) { dispatch(edgesChanged(changes)); @@ -130,7 +132,7 @@ export const Flow = memo(() => { onCloseGlobal(); }, [onCloseGlobal]); - const onInit: OnInit = useCallback((flow) => { + const onInit: OnInit = useCallback((flow) => { $flow.set(flow); flow.fitView(); }, []); @@ -158,13 +160,13 @@ export const Flow = memo(() => { * where the edge is deleted if you click it accidentally). */ - const onEdgeUpdateStart: NonNullable = useCallback((e, edge, _handleType) => { + const onReconnectStart = useCallback((event: MouseEvent, edge: AnyEdge, _handleType: HandleType) => { $edgePendingUpdate.set(edge); $didUpdateEdge.set(false); - $lastEdgeUpdateMouseEvent.set(e); + $lastEdgeUpdateMouseEvent.set(event); }, []); - const onEdgeUpdate: OnEdgeUpdateFunc = useCallback( + const onReconnect: OnReconnect = useCallback( (oldEdge, newConnection) => { // This event is fired when an edge update is successful $didUpdateEdge.set(true); @@ -183,7 +185,7 @@ export const Flow = memo(() => { [dispatch, updateNodeInternals] ); - const onEdgeUpdateEnd: NonNullable = useCallback( + const onReconnectEnd: NonNullable = useCallback( (e, edge, _handleType) => { const didUpdateEdge = $didUpdateEdge.get(); // Fall back to a reasonable default event @@ -208,7 +210,7 @@ export const Flow = memo(() => { // #endregion - const { copySelection, pasteSelection, pasteSelectionWithEdges } = useCopyPaste(); + const { copySelection, pasteSelection, pasteSelectionWithEdges } = useNodeCopyPaste(); useRegisteredHotkeys({ id: 'copySelection', @@ -220,8 +222,8 @@ export const Flow = memo(() => { const selectAll = useCallback(() => { const { nodes, edges } = selectNodesSlice(store.getState()); - const nodeChanges: NodeChange[] = []; - const edgeChanges: EdgeChange[] = []; + const nodeChanges: NodeChange[] = []; + const edgeChanges: EdgeChange[] = []; nodes.forEach(({ id, selected }) => { if (!selected) { nodeChanges.push({ type: 'select', id, selected: true }); @@ -294,8 +296,8 @@ export const Flow = memo(() => { const deleteSelection = useCallback(() => { const { nodes, edges } = selectNodesSlice(store.getState()); - const nodeChanges: NodeChange[] = []; - const edgeChanges: EdgeChange[] = []; + const nodeChanges: NodeChange[] = []; + const edgeChanges: EdgeChange[] = []; nodes .filter((n) => n.selected) .forEach(({ id }) => { @@ -322,7 +324,7 @@ export const Flow = memo(() => { }); return ( - id="workflow-editor" ref={flowWrapper} defaultViewport={viewport} @@ -334,9 +336,9 @@ export const Flow = memo(() => { onMouseMove={onMouseMove} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} - onEdgeUpdate={onEdgeUpdate} - onEdgeUpdateStart={onEdgeUpdateStart} - onEdgeUpdateEnd={onEdgeUpdateEnd} + onReconnect={onReconnect} + onReconnectStart={onReconnectStart} + onReconnectEnd={onReconnectEnd} onConnectStart={onConnectStart} onConnect={onConnect} onConnectEnd={onConnectEnd} diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx index b55fc6fe70a..fa309559acb 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx @@ -1,4 +1,6 @@ import { useStore } from '@nanostores/react'; +import type { ConnectionLineComponentProps } from '@xyflow/react'; +import { getBezierPath } from '@xyflow/react'; import { useAppSelector } from 'app/store/storeHooks'; import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar'; import { getFieldColor } from 'features/nodes/components/flow/edges/util/getEdgeColor'; @@ -6,8 +8,6 @@ import { $pendingConnection } from 'features/nodes/store/nodesSlice'; import { selectShouldAnimateEdges, selectShouldColorEdges } from 'features/nodes/store/workflowSettingsSlice'; import type { CSSProperties } from 'react'; import { memo, useMemo } from 'react'; -import type { ConnectionLineComponentProps } from 'reactflow'; -import { getBezierPath } from 'reactflow'; const pathStyles: CSSProperties = { opacity: 0.8 }; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx index 0d7e7b7d5e4..17c44717f14 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx @@ -1,13 +1,39 @@ -import { Badge, Flex } from '@invoke-ai/ui-library'; -import { useStore } from '@nanostores/react'; +import type { SystemStyleObject } from '@invoke-ai/ui-library'; +import { Badge, Box, chakra } from '@invoke-ai/ui-library'; +import type { EdgeProps } from '@xyflow/react'; +import { BaseEdge, EdgeLabelRenderer, getBezierPath } from '@xyflow/react'; import { useAppSelector } from 'app/store/storeHooks'; -import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens'; -import { getEdgeStyles } from 'features/nodes/components/flow/edges/util/getEdgeColor'; -import { makeEdgeSelector } from 'features/nodes/components/flow/edges/util/makeEdgeSelector'; -import { $templates } from 'features/nodes/store/nodesSlice'; +import { buildSelectAreConnectedNodesSelected } from 'features/nodes/components/flow/edges/util/buildEdgeSelectors'; +import { selectShouldAnimateEdges } from 'features/nodes/store/workflowSettingsSlice'; +import type { CollapsedInvocationNodeEdge } from 'features/nodes/types/invocation'; import { memo, useMemo } from 'react'; -import type { EdgeProps } from 'reactflow'; -import { BaseEdge, EdgeLabelRenderer, getBezierPath } from 'reactflow'; + +const ChakraBaseEdge = chakra(BaseEdge); + +const baseEdgeSx: SystemStyleObject = { + strokeWidth: '3px !important', + stroke: 'base.500 !important', + opacity: '0.5 !important', + strokeDasharray: 'none', + '&[data-selected="true"]': { + opacity: '1 !important', + }, + '&[data-selected="true"], &[data-are-connected-nodes-selected="true"]': { + strokeDasharray: '5 !important', + }, + '&[data-should-animate-edges="true"]': { + animation: 'dashdraw 0.5s linear infinite !important', + }, +}; + +const badgeSx: SystemStyleObject = { + bg: 'base.500', + opacity: 0.5, + shadow: 'base', + '&[data-selected="true"]': { + opacity: 1, + }, +}; const InvocationCollapsedEdge = ({ sourceX, @@ -20,17 +46,15 @@ const InvocationCollapsedEdge = ({ data, selected = false, source, - sourceHandleId, target, - targetHandleId, -}: EdgeProps<{ count: number }>) => { - const templates = useStore($templates); - const selector = useMemo( - () => makeEdgeSelector(templates, source, sourceHandleId, target, targetHandleId), - [templates, source, sourceHandleId, target, targetHandleId] +}: EdgeProps) => { + const shouldAnimateEdges = useAppSelector(selectShouldAnimateEdges); + const selectAreConnectedNodesSelected = useMemo( + () => buildSelectAreConnectedNodesSelected(source, target), + [source, target] ); - const { shouldAnimateEdges, areConnectedNodesSelected } = useAppSelector(selector); + const areConnectedNodesSelected = useAppSelector(selectAreConnectedNodesSelected); const [edgePath, labelX, labelY] = getBezierPath({ sourceX, @@ -41,31 +65,29 @@ const InvocationCollapsedEdge = ({ targetPosition, }); - const { base500 } = useChakraThemeTokens(); - - const edgeStyles = useMemo( - () => getEdgeStyles(base500, selected, shouldAnimateEdges, areConnectedNodesSelected), - [areConnectedNodesSelected, base500, selected, shouldAnimateEdges] - ); - return ( <> - - {data?.count && data.count > 1 && ( + + {data?.count !== undefined && ( - - + {data.count} - + )} diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx index f9e4deed9e4..bd7484f0beb 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx @@ -1,14 +1,61 @@ -import { Flex, Text } from '@invoke-ai/ui-library'; +import type { SystemStyleObject } from '@invoke-ai/ui-library'; +import { chakra, Flex, Text } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import type { EdgeProps } from '@xyflow/react'; +import { BaseEdge, EdgeLabelRenderer, getBezierPath } from '@xyflow/react'; import { useAppSelector } from 'app/store/storeHooks'; -import { getEdgeStyles } from 'features/nodes/components/flow/edges/util/getEdgeColor'; import { $templates } from 'features/nodes/store/nodesSlice'; -import { selectShouldShowEdgeLabels } from 'features/nodes/store/workflowSettingsSlice'; +import { selectShouldAnimateEdges, selectShouldShowEdgeLabels } from 'features/nodes/store/workflowSettingsSlice'; +import type { DefaultInvocationNodeEdge } from 'features/nodes/types/invocation'; import { memo, useMemo } from 'react'; -import type { EdgeProps } from 'reactflow'; -import { BaseEdge, EdgeLabelRenderer, getBezierPath } from 'reactflow'; -import { makeEdgeSelector } from './util/makeEdgeSelector'; +import { + buildSelectAreConnectedNodesSelected, + buildSelectEdgeColor, + buildSelectEdgeLabel, +} from './util/buildEdgeSelectors'; + +const ChakraBaseEdge = chakra(BaseEdge); + +const baseEdgeSx: SystemStyleObject = { + strokeWidth: '3px !important', + opacity: '0.5 !important', + strokeDasharray: 'none', + '&[data-selected="true"]': { + opacity: '1 !important', + }, + '&[data-should-animate-edges="true"]': { + animation: 'dashdraw 0.5s linear infinite !important', + '&[data-selected="true"], &[data-are-connected-nodes-selected="true"]': { + strokeDasharray: '5 !important', + }, + }, +}; + +const edgeLabelWrapperSx: SystemStyleObject = { + pointerEvents: 'all', + position: 'absolute', + bg: 'base.800', + borderRadius: 'base', + borderWidth: 1, + opacity: 0.5, + borderColor: 'transparent', + py: 1, + px: 3, + shadow: 'md', + '&[data-selected="true"]': { + opacity: 1, + borderColor: undefined, + }, +}; + +const edgeLabelTextSx: SystemStyleObject = { + fontWeight: 'semibold', + color: 'base.300', + '&[data-selected="true"]': { + color: 'base.100', + }, +}; const InvocationDefaultEdge = ({ sourceX, @@ -23,15 +70,26 @@ const InvocationDefaultEdge = ({ target, sourceHandleId, targetHandleId, -}: EdgeProps) => { +}: EdgeProps) => { const templates = useStore($templates); - const selector = useMemo( - () => makeEdgeSelector(templates, source, sourceHandleId, target, targetHandleId), + const shouldAnimateEdges = useAppSelector(selectShouldAnimateEdges); + const shouldShowEdgeLabels = useAppSelector(selectShouldShowEdgeLabels); + + const selectAreConnectedNodesSelected = useMemo( + () => buildSelectAreConnectedNodesSelected(source, target), + [source, target] + ); + const selectStrokeColor = useMemo( + () => buildSelectEdgeColor(templates, source, sourceHandleId, target, targetHandleId), [templates, source, sourceHandleId, target, targetHandleId] ); - - const { shouldAnimateEdges, areConnectedNodesSelected, stroke, label } = useAppSelector(selector); - const shouldShowEdgeLabels = useAppSelector(selectShouldShowEdgeLabels); + const selectEdgeLabel = useMemo( + () => buildSelectEdgeLabel(templates, source, sourceHandleId, target, targetHandleId), + [templates, source, sourceHandleId, target, targetHandleId] + ); + const areConnectedNodesSelected = useAppSelector(selectAreConnectedNodesSelected); + const stroke = useAppSelector(selectStrokeColor); + const label = useAppSelector(selectEdgeLabel); const [edgePath, labelX, labelY] = getBezierPath({ sourceX, @@ -42,31 +100,26 @@ const InvocationDefaultEdge = ({ targetPosition, }); - const edgeStyles = useMemo( - () => getEdgeStyles(stroke, selected, shouldAnimateEdges, areConnectedNodesSelected), - [areConnectedNodesSelected, stroke, selected, shouldAnimateEdges] - ); - return ( <> - + {label && shouldShowEdgeLabels && ( - + {label} diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/buildEdgeSelectors.ts b/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/buildEdgeSelectors.ts new file mode 100644 index 00000000000..9e4aeea5e39 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/buildEdgeSelectors.ts @@ -0,0 +1,62 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar'; +import { selectNodesSlice } from 'features/nodes/store/selectors'; +import type { Templates } from 'features/nodes/store/types'; +import { selectWorkflowSettingsSlice } from 'features/nodes/store/workflowSettingsSlice'; +import { isInvocationNode } from 'features/nodes/types/invocation'; + +import { getFieldColor } from './getEdgeColor'; + +export const buildSelectAreConnectedNodesSelected = (source: string, target: string) => + createSelector(selectNodesSlice, (nodes): boolean => { + const sourceNode = nodes.nodes.find((node) => node.id === source); + const targetNode = nodes.nodes.find((node) => node.id === target); + + return Boolean(sourceNode?.selected || targetNode?.selected); + }); + +export const buildSelectEdgeColor = ( + templates: Templates, + source: string, + sourceHandleId: string | null | undefined, + target: string, + targetHandleId: string | null | undefined +) => + createSelector(selectNodesSlice, selectWorkflowSettingsSlice, (nodes, workflowSettings): string => { + const { shouldColorEdges } = workflowSettings; + const sourceNode = nodes.nodes.find((node) => node.id === source); + const targetNode = nodes.nodes.find((node) => node.id === target); + + if (!sourceNode || !sourceHandleId || !targetNode || !targetHandleId) { + return colorTokenToCssVar('base.500'); + } + + const sourceNodeTemplate = templates[sourceNode.data.type]; + + const isInvocationToInvocationEdge = isInvocationNode(sourceNode) && isInvocationNode(targetNode); + const outputFieldTemplate = sourceNodeTemplate?.outputs[sourceHandleId]; + const sourceType = isInvocationToInvocationEdge ? outputFieldTemplate?.type : undefined; + + return sourceType && shouldColorEdges ? getFieldColor(sourceType) : colorTokenToCssVar('base.500'); + }); + +export const buildSelectEdgeLabel = ( + templates: Templates, + source: string, + sourceHandleId: string | null | undefined, + target: string, + targetHandleId: string | null | undefined +) => + createSelector(selectNodesSlice, (nodes): string | null => { + const sourceNode = nodes.nodes.find((node) => node.id === source); + const targetNode = nodes.nodes.find((node) => node.id === target); + + if (!sourceNode || !sourceHandleId || !targetNode || !targetHandleId) { + return null; + } + + const sourceNodeTemplate = templates[sourceNode.data.type]; + const targetNodeTemplate = templates[targetNode.data.type]; + + return `${sourceNodeTemplate?.title || sourceNode.data?.label} -> ${targetNodeTemplate?.title || targetNode.data?.label}`; + }); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/getEdgeColor.ts b/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/getEdgeColor.ts index b5801c45edb..e7fa43015b2 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/getEdgeColor.ts +++ b/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/getEdgeColor.ts @@ -1,7 +1,6 @@ import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar'; import { FIELD_COLORS } from 'features/nodes/types/constants'; import type { FieldType } from 'features/nodes/types/field'; -import type { CSSProperties } from 'react'; export const getFieldColor = (fieldType: FieldType | null): string => { if (!fieldType) { @@ -11,16 +10,3 @@ export const getFieldColor = (fieldType: FieldType | null): string => { return color ? colorTokenToCssVar(color) : colorTokenToCssVar('base.500'); }; - -export const getEdgeStyles = ( - stroke: string, - selected: boolean, - shouldAnimateEdges: boolean, - areConnectedNodesSelected: boolean -): CSSProperties => ({ - strokeWidth: 3, - stroke, - opacity: selected ? 1 : 0.5, - animation: shouldAnimateEdges ? 'dashdraw 0.5s linear infinite' : undefined, - strokeDasharray: selected || areConnectedNodesSelected ? 5 : 'none', -}); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts b/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts deleted file mode 100644 index 6a783f31581..00000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar'; -import { deepClone } from 'common/util/deepClone'; -import { selectNodesSlice } from 'features/nodes/store/selectors'; -import type { Templates } from 'features/nodes/store/types'; -import { selectWorkflowSettingsSlice } from 'features/nodes/store/workflowSettingsSlice'; -import { isInvocationNode } from 'features/nodes/types/invocation'; - -import { getFieldColor } from './getEdgeColor'; - -const defaultReturnValue = { - areConnectedNodesSelected: false, - shouldAnimateEdges: false, - stroke: colorTokenToCssVar('base.500'), - label: '', -}; - -export const makeEdgeSelector = ( - templates: Templates, - source: string, - sourceHandleId: string | null | undefined, - target: string, - targetHandleId: string | null | undefined -) => - createMemoizedSelector( - selectNodesSlice, - selectWorkflowSettingsSlice, - ( - nodes, - workflowSettings - ): { areConnectedNodesSelected: boolean; shouldAnimateEdges: boolean; stroke: string; label: string } => { - const { shouldAnimateEdges, shouldColorEdges } = workflowSettings; - const sourceNode = nodes.nodes.find((node) => node.id === source); - const targetNode = nodes.nodes.find((node) => node.id === target); - - const returnValue = deepClone(defaultReturnValue); - returnValue.shouldAnimateEdges = shouldAnimateEdges; - - const isInvocationToInvocationEdge = isInvocationNode(sourceNode) && isInvocationNode(targetNode); - - returnValue.areConnectedNodesSelected = Boolean(sourceNode?.selected || targetNode?.selected); - if (!sourceNode || !sourceHandleId || !targetNode || !targetHandleId) { - return returnValue; - } - - const sourceNodeTemplate = templates[sourceNode.data.type]; - const targetNodeTemplate = templates[targetNode.data.type]; - - const outputFieldTemplate = sourceNodeTemplate?.outputs[sourceHandleId]; - const sourceType = isInvocationToInvocationEdge ? outputFieldTemplate?.type : undefined; - - returnValue.stroke = sourceType && shouldColorEdges ? getFieldColor(sourceType) : colorTokenToCssVar('base.500'); - - returnValue.label = `${sourceNodeTemplate?.title || sourceNode.data?.label} -> ${targetNodeTemplate?.title || targetNode.data?.label}`; - - return returnValue; - } - ); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx index f7587252569..d89e452dfda 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx @@ -1,5 +1,6 @@ import { Flex, Image, Text } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import type { NodeProps } from '@xyflow/react'; import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import { DndImage } from 'features/dnd/DndImage'; @@ -12,7 +13,6 @@ import { motion } from 'framer-motion'; import type { CSSProperties, PropsWithChildren } from 'react'; import { memo, useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import type { NodeProps } from 'reactflow'; import { $lastProgressEvent } from 'services/events/stores'; const CurrentImageNode = (props: NodeProps) => { diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx index baa7fc262a8..fe3c292ad79 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx @@ -1,13 +1,14 @@ import { Flex, Grid, GridItem } from '@invoke-ai/ui-library'; import NodeWrapper from 'features/nodes/components/flow/nodes/common/NodeWrapper'; -import { InvocationInputFieldCheck } from 'features/nodes/components/flow/nodes/Invocation/fields/InvocationFieldCheck'; -import { useFieldNames } from 'features/nodes/hooks/useFieldNames'; +import { InputFieldGate } from 'features/nodes/components/flow/nodes/Invocation/fields/InputFieldGate'; +import { OutputFieldGate } from 'features/nodes/components/flow/nodes/Invocation/fields/OutputFieldGate'; +import { OutputFieldNodesEditorView } from 'features/nodes/components/flow/nodes/Invocation/fields/OutputFieldNodesEditorView'; +import { useInputFieldNamesByStatus } from 'features/nodes/hooks/useInputFieldNamesByStatus'; import { useOutputFieldNames } from 'features/nodes/hooks/useOutputFieldNames'; import { useWithFooter } from 'features/nodes/hooks/useWithFooter'; import { memo } from 'react'; -import InputField from './fields/InputField'; -import OutputField from './fields/OutputField'; +import { InputFieldEditModeNodes } from './fields/InputFieldEditModeNodes'; import InvocationNodeFooter from './InvocationNodeFooter'; import InvocationNodeHeader from './InvocationNodeHeader'; @@ -20,7 +21,7 @@ type Props = { }; const InvocationNode = ({ nodeId, isOpen, label, type, selected }: Props) => { - const fieldNames = useFieldNames(nodeId); + const fieldNames = useInputFieldNamesByStatus(nodeId); const withFooter = useWithFooter(nodeId); const outputFieldNames = useOutputFieldNames(nodeId); @@ -42,34 +43,28 @@ const InvocationNode = ({ nodeId, isOpen, label, type, selected }: Props) => { {fieldNames.connectionFields.map((fieldName, i) => ( - - - + + + ))} {outputFieldNames.map((fieldName, i) => ( - + + + ))} {fieldNames.anyOrDirectFields.map((fieldName) => ( - - - + + + ))} {fieldNames.missingFields.map((fieldName) => ( - - - + + + ))} diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeCollapsedHandles.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeCollapsedHandles.tsx index b888e8a5162..456f89daa04 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeCollapsedHandles.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeCollapsedHandles.tsx @@ -1,40 +1,25 @@ -import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens'; +import { Handle, Position } from '@xyflow/react'; import { useNodeTemplate } from 'features/nodes/hooks/useNodeTemplate'; import { map } from 'lodash-es'; import type { CSSProperties } from 'react'; -import { memo, useMemo } from 'react'; -import { Handle, Position } from 'reactflow'; +import { memo } from 'react'; interface Props { nodeId: string; } const hiddenHandleStyles: CSSProperties = { visibility: 'hidden' }; +const collapsedHandleStyles: CSSProperties = { + borderWidth: 0, + borderRadius: '3px', + width: '1rem', + height: '1rem', + backgroundColor: 'var(--invoke-colors-base-600)', + zIndex: -1, +}; const InvocationNodeCollapsedHandles = ({ nodeId }: Props) => { const template = useNodeTemplate(nodeId); - const { base600 } = useChakraThemeTokens(); - - const dummyHandleStyles: CSSProperties = useMemo( - () => ({ - borderWidth: 0, - borderRadius: '3px', - width: '1rem', - height: '1rem', - backgroundColor: base600, - zIndex: -1, - }), - [base600] - ); - - const collapsedTargetStyles: CSSProperties = useMemo( - () => ({ ...dummyHandleStyles, left: '-0.5rem' }), - [dummyHandleStyles] - ); - const collapsedSourceStyles: CSSProperties = useMemo( - () => ({ ...dummyHandleStyles, right: '-0.5rem' }), - [dummyHandleStyles] - ); if (!template) { return null; @@ -47,7 +32,7 @@ const InvocationNodeCollapsedHandles = ({ nodeId }: Props) => { id={`${nodeId}-collapsed-target`} isConnectable={false} position={Position.Left} - style={collapsedTargetStyles} + style={collapsedHandleStyles} /> {map(template.inputs, (input) => ( { id={`${nodeId}-collapsed-source`} isConnectable={false} position={Position.Right} - style={collapsedSourceStyles} + style={collapsedHandleStyles} /> {map(template.outputs, (output) => ( { - const hasImageOutput = useHasImageOutput(nodeId); + const hasImageOutput = useNodeHasImageOutput(nodeId); const isCacheEnabled = useFeatureStatus('invocationCache'); return ( { +export const InvocationNodeInfoIcon = memo(({ nodeId }: Props) => { const needsUpdate = useNodeNeedsUpdate(nodeId); return ( @@ -20,96 +21,66 @@ const InvocationNodeInfoIcon = ({ nodeId }: Props) => { ); -}; +}); -export default memo(InvocationNodeInfoIcon); +InvocationNodeInfoIcon.displayName = 'InvocationNodeInfoIcon'; const TooltipContent = memo(({ nodeId }: { nodeId: string }) => { - const node = useNode(nodeId); + const notes = useInvocationNodeNotes(nodeId); + const label = useNodeLabel(nodeId); + const version = useNodeVersion(nodeId); const nodeTemplate = useNodeTemplate(nodeId); const { t } = useTranslation(); const title = useMemo(() => { - if (node.data?.label && nodeTemplate?.title) { - return `${node.data.label} (${nodeTemplate.title})`; - } - - if (node.data?.label && !nodeTemplate) { - return node.data.label; - } - - if (!node.data?.label && nodeTemplate) { - return nodeTemplate.title; - } - - return t('nodes.unknownNode'); - }, [node.data.label, nodeTemplate, t]); - - const versionComponent = useMemo(() => { - if (!isInvocationNode(node) || !nodeTemplate) { - return null; - } - - if (!node.data.version) { - return ( - - {t('nodes.versionUnknown')} - - ); - } - - if (!nodeTemplate.version) { - return ( - - {t('nodes.version')} {node.data.version} ({t('nodes.unknownTemplate')}) - - ); - } - - if (compare(node.data.version, nodeTemplate.version, '<')) { - return ( - - {t('nodes.version')} {node.data.version} ({t('nodes.updateNode')}) - - ); + if (label) { + return `${label} (${nodeTemplate.title})`; } - if (compare(node.data.version, nodeTemplate.version, '>')) { - return ( - - {t('nodes.version')} {node.data.version} ({t('nodes.updateApp')}) - - ); - } - - return ( - - {t('nodes.version')} {node.data.version} - - ); - }, [node, nodeTemplate, t]); - - if (!isInvocationNode(node)) { - return {t('nodes.unknownNode')}; - } + return nodeTemplate.title; + }, [label, nodeTemplate.title]); return ( {title} - {nodeTemplate?.nodePack && ( - - {t('nodes.nodePack')}: {nodeTemplate.nodePack} - - )} + + {t('nodes.nodePack')}: {nodeTemplate.nodePack} + - {nodeTemplate?.description} + {nodeTemplate.description} - {versionComponent} - {node.data?.notes && {node.data.notes}} + + {notes && {notes}} ); }); TooltipContent.displayName = 'TooltipContent'; + +const Version = ({ nodeVersion, templateVersion }: { nodeVersion: string; templateVersion: string }) => { + const { t } = useTranslation(); + + if (compare(nodeVersion, templateVersion, '<')) { + return ( + + {t('nodes.version')} {nodeVersion} ({t('nodes.updateNode')}) + + ); + } + + if (compare(nodeVersion, templateVersion, '>')) { + return ( + + {t('nodes.version')} {nodeVersion} ({t('nodes.updateApp')}) + + ); + } + + return ( + + {t('nodes.version')} {nodeVersion} + + ); +}; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/NotesTextarea.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeNotesTextarea.tsx similarity index 64% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/NotesTextarea.tsx rename to invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeNotesTextarea.tsx index 9f4da396af1..8e316e7841b 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/NotesTextarea.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeNotesTextarea.tsx @@ -1,31 +1,31 @@ import { FormControl, FormLabel, Textarea } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; -import { useNode } from 'features/nodes/hooks/useNode'; +import { useInvocationNodeNotes } from 'features/nodes/hooks/useNodeNotes'; import { nodeNotesChanged } from 'features/nodes/store/nodesSlice'; -import { isInvocationNode } from 'features/nodes/types/invocation'; import type { ChangeEvent } from 'react'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -const NotesTextarea = ({ nodeId }: { nodeId: string }) => { +type Props = { + nodeId: string; +}; + +export const InvocationNodeNotesTextarea = memo(({ nodeId }: Props) => { const dispatch = useAppDispatch(); - const node = useNode(nodeId); const { t } = useTranslation(); + const notes = useInvocationNodeNotes(nodeId); const handleNotesChanged = useCallback( (e: ChangeEvent) => { dispatch(nodeNotesChanged({ nodeId, notes: e.target.value })); }, [dispatch, nodeId] ); - if (!isInvocationNode(node)) { - return null; - } return ( {t('nodes.notes')} -