diff --git a/docs/contributing/frontend/WORKFLOWS.md b/docs/contributing/frontend/WORKFLOWS.md index 533419e070..86d541e340 100644 --- a/docs/contributing/frontend/WORKFLOWS.md +++ b/docs/contributing/frontend/WORKFLOWS.md @@ -299,7 +299,7 @@ Migration logic is in [migrations.ts]. [pydantic]: https://github.com/pydantic/pydantic 'pydantic' [zod]: https://github.com/colinhacks/zod 'zod' [openapi-types]: https://github.com/kogosoftwarellc/open-api/tree/main/packages/openapi-types 'openapi-types' -[reactflow]: https://github.com/xyflow/xyflow 'reactflow' +[reactflow]: https://github.com/xyflow/xyflow '@xyflow/react' [reactflow-concepts]: https://reactflow.dev/learn/concepts/terms-and-definitions [reactflow-events]: https://reactflow.dev/api-reference/react-flow#event-handlers [buildWorkflow.ts]: https://github.com/invoke-ai/InvokeAI/blob/main/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json index 879985accd..e9e6267dfd 100644 --- a/invokeai/frontend/web/package.json +++ b/invokeai/frontend/web/package.json @@ -63,6 +63,7 @@ "@nanostores/react": "^0.7.2", "@reduxjs/toolkit": "2.2.3", "@roarr/browser-log-writer": "^1.3.0", + "@xyflow/react": "^12.0.4", "chakra-react-select": "^4.7.6", "compare-versions": "^6.1.0", "dateformat": "^5.0.3", @@ -94,7 +95,6 @@ "react-select": "5.8.0", "react-use": "^17.5.0", "react-virtuoso": "^4.7.10", - "reactflow": "^11.11.3", "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 64189f0d82..bb319be8ba 100644 --- a/invokeai/frontend/web/pnpm-lock.yaml +++ b/invokeai/frontend/web/pnpm-lock.yaml @@ -41,6 +41,9 @@ dependencies: '@roarr/browser-log-writer': specifier: ^1.3.0 version: 1.3.0 + '@xyflow/react': + specifier: ^12.0.4 + version: 12.0.4(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) chakra-react-select: specifier: ^4.7.6 version: 4.7.6(@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.11.4)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) @@ -134,9 +137,6 @@ dependencies: react-virtuoso: specifier: ^4.7.10 version: 4.7.10(react-dom@18.3.1)(react@18.3.1) - reactflow: - specifier: ^11.11.3 - version: 11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) redux-dynamic-middlewares: specifier: ^2.2.0 version: 2.2.0 @@ -3874,114 +3874,6 @@ packages: react: 18.3.1 dev: true - /@reactflow/background@11.3.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-hkvpVEhgvfTDyCvdlitw4ioKCYLaaiRXnuEG+1QM3Np+7N1DiWF1XOv5I8AFyNoJL07yXEkbECUTsHvkBvcG5A==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.11.3(@types/react@18.3.1)(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.2(@types/react@18.3.1)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/controls@11.2.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-3xgEg6ALIVkAQCS4NiBjb7ad8Cb3D8CtA7Vvl4Hf5Ar2PIVs6FOaeft9s2iDZGtsWP35ECDYId1rIFVhQL8r+A==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.11.3(@types/react@18.3.1)(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.2(@types/react@18.3.1)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/core@11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-+adHdUa7fJSEM93fWfjQwyWXeI92a1eLKwWbIstoCakHpL8UjzwhEh6sn+mN2h/59MlVI7Ehr1iGTt3MsfcIFA==} - 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.2(@types/react@18.3.1)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/minimap@11.7.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-m2MvdiGSyOu44LEcERDEl1Aj6x//UQRWo3HEAejNU4HQTlJnYrSN8tgrYF8TxC1+c/9UdyzQY5VYgrTwW4QWdg==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.11.3(@types/react@18.3.1)(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.2(@types/react@18.3.1)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/node-resizer@2.2.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-X7ceQ2s3jFLgbkg03n2RYr4hm3jTVrzkW2W/8ANv/SZfuVmF8XJxlERuD8Eka5voKqLda0ywIZGAbw9GoHLfUQ==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.11.3(@types/react@18.3.1)(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.2(@types/react@18.3.1)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - - /@reactflow/node-toolbar@1.3.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-aknvNICO10uWdthFSpgD6ctY/CTBeJUMV9co8T9Ilugr08Nb89IQ4uD0dPmr031ewMQxixtYIkw+sSDDzd2aaQ==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/core': 11.11.3(@types/react@18.3.1)(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.2(@types/react@18.3.1)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - /@reduxjs/toolkit@2.2.3(react-redux@9.1.2)(react@18.3.1): resolution: {integrity: sha512-76dll9EnJXg4EVcI5YNxZA/9hSAmZsFqzMmNRHvIlzw2WS/twfcVX3ysYrWGJMClwEmChQFC4yRq74tn6fdzRA==} peerDependencies: @@ -5233,137 +5125,26 @@ packages: '@types/node': 20.12.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.9: - resolution: {integrity: sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==} - 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: @@ -5377,41 +5158,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.9 - '@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 @@ -5481,10 +5227,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: @@ -6008,6 +5750,34 @@ packages: resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} dev: false + /@xyflow/react@12.0.4(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-eeQzw1gIbLKOB55rp2+20uB1PASDUf1q6zy2VsgugnuPEcL/olVMX3WT42XxyG8m3rcbUiHlq2NSmPTFWEjiUQ==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@xyflow/system': 0.0.37 + classcat: 5.0.5 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.2(@types/react@18.3.1)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + + /@xyflow/system@0.0.37: + resolution: {integrity: sha512-hSIhezhxgftPUpC+xiQVIorcRILZUOWlLjpYPTyGWRu8s4RJvM4GqvrsFmD5OnMKXLgpU7/PqqUibDVO67oWQQ==} + 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 + /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.20.2): resolution: {integrity: sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==} engines: {node: '>=14.15.0'} @@ -11318,25 +11088,6 @@ packages: dependencies: loose-envify: 1.4.0 - /reactflow@11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-wusd1Xpn1wgsSEv7UIa4NNraCwH9syBtubBy4xVNXg3b+CDKM+sFaF3hnMx0tr0et4km9urIDdNvwm34QiZong==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - dependencies: - '@reactflow/background': 11.3.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) - '@reactflow/controls': 11.2.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) - '@reactflow/core': 11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) - '@reactflow/minimap': 11.7.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) - '@reactflow/node-resizer': 2.2.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) - '@reactflow/node-toolbar': 1.3.13(@types/react@18.3.1)(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 - /read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} diff --git a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts index 7ea5311585..423af642b3 100644 --- a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts +++ b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts @@ -1,4 +1,5 @@ import { useStore } from '@nanostores/react'; +import { getConnectedEdges } from '@xyflow/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import { @@ -22,7 +23,6 @@ import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import i18n from 'i18next'; import { forEach, upperFirst } from 'lodash-es'; import { useMemo } from 'react'; -import { getConnectedEdges } from 'reactflow'; const LAYER_TYPE_TO_TKEY: Record = { initial_image_layer: 'controlLayers.globalInitialImage', diff --git a/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx b/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx index 737adb52e7..97040c3814 100644 --- a/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/NodeEditor.tsx @@ -1,4 +1,4 @@ -import 'reactflow/dist/style.css'; +import '@xyflow/react/dist/style.css'; import { Flex } from '@invoke-ai/ui-library'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx index 6da87f4e98..32b9d96d67 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx @@ -1,8 +1,7 @@ -import 'reactflow/dist/style.css'; - import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library'; import { Combobox, Flex, Popover, PopoverAnchor, PopoverBody, PopoverContent } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import type { EdgeChange, NodeChange } from '@xyflow/react'; import { useAppDispatch, useAppStore } from 'app/store/storeHooks'; import type { SelectInstance } from 'chakra-react-select'; import { useBuildNode } from 'features/nodes/hooks/useBuildNode'; @@ -21,7 +20,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 { AnyNode } from 'features/nodes/types/invocation'; +import type { AppNode } from 'features/nodes/types/invocation'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { toast } from 'features/toast/toast'; import { filter, map, memoize, some } from 'lodash-es'; @@ -31,7 +30,6 @@ import { useHotkeys } from 'react-hotkeys-hook'; import type { HotkeyCallback } from 'react-hotkeys-hook/dist/types'; import { useTranslation } from 'react-i18next'; import type { FilterOptionOption } from 'react-select/dist/declarations/src/filters'; -import type { EdgeChange, NodeChange } from 'reactflow'; const createRegex = memoize( (inputValue: string) => @@ -120,7 +118,7 @@ const AddNodePopover = () => { }, [filteredTemplates, pendingConnection, t]); const addNode = useCallback( - (nodeType: string): AnyNode | null => { + (nodeType: string): AppNode | null => { const node = buildInvocation(nodeType); if (!node) { const errorMessage = t('nodes.unknownNode', { 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 727dad9617..7c5ab4c204 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx @@ -1,5 +1,20 @@ import { useGlobalMenuClose, useToken } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import type { + EdgeChange, + EdgeTypes, + NodeChange, + NodeTypes, + 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 { useConnection } from 'features/nodes/hooks/useConnection'; import { useCopyPaste } from 'features/nodes/hooks/useCopyPaste'; @@ -24,36 +39,23 @@ import { connectionToEdge } from 'features/nodes/store/util/reactFlowUtil'; 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'; import InvocationDefaultEdge from './edges/InvocationDefaultEdge'; import CurrentImageNode from './nodes/CurrentImage/CurrentImageNode'; import InvocationNodeWrapper from './nodes/Invocation/InvocationNodeWrapper'; -import NotesNode from './nodes/Notes/NotesNode'; +import { NotesNodeComponent } from './nodes/Notes/NotesNode'; -const edgeTypes = { +const edgeTypes: EdgeTypes = { collapsed: InvocationCollapsedEdge, default: InvocationDefaultEdge, }; -const nodeTypes = { +const nodeTypes: NodeTypes = { invocation: InvocationNodeWrapper, current_image: CurrentImageNode, - notes: NotesNode, + notes: NotesNodeComponent, }; // TODO: can we support reactflow? if not, we could style the attribution so it matches the app @@ -151,13 +153,13 @@ export const Flow = memo(() => { * where the edge is deleted if you click it accidentally). */ - const onEdgeUpdateStart: NonNullable = useCallback((e, edge, _handleType) => { + const onReconnectStart: NonNullable = useCallback((e, edge, _handleType) => { $edgePendingUpdate.set(edge); $didUpdateEdge.set(false); $lastEdgeUpdateMouseEvent.set(e); }, []); - const onEdgeUpdate: OnEdgeUpdateFunc = useCallback( + const onReconnect: OnReconnect = useCallback( (oldEdge, newConnection) => { // This event is fired when an edge update is successful $didUpdateEdge.set(true); @@ -176,7 +178,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 @@ -316,9 +318,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 09c88c713b..116af567e8 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,12 +1,12 @@ 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'; import { $pendingConnection } from 'features/nodes/store/nodesSlice'; 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 0d7e7b7d5e..764d19b768 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,15 @@ import { Badge, Flex } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import type { Edge, 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 { memo, useMemo } from 'react'; -import type { EdgeProps } from 'reactflow'; -import { BaseEdge, EdgeLabelRenderer, getBezierPath } from 'reactflow'; + +export type CollapsedEdge = Edge<{ count: number }>; const InvocationCollapsedEdge = ({ sourceX, @@ -23,7 +25,7 @@ const InvocationCollapsedEdge = ({ sourceHandleId, target, targetHandleId, -}: EdgeProps<{ count: number }>) => { +}: EdgeProps) => { const templates = useStore($templates); const selector = useMemo( () => makeEdgeSelector(templates, source, sourceHandleId, target, targetHandleId), 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 5a27e974e5..41387f5e5b 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,11 +1,11 @@ import { 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 { memo, useMemo } from 'react'; -import type { EdgeProps } from 'reactflow'; -import { BaseEdge, EdgeLabelRenderer, getBezierPath } from 'reactflow'; import { makeEdgeSelector } from './util/makeEdgeSelector'; 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 968136a3c2..4f0678066f 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,4 +1,5 @@ import { Flex, Image, Text } from '@invoke-ai/ui-library'; +import type { NodeProps } from '@xyflow/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import IAIDndImage from 'common/components/IAIDndImage'; @@ -13,7 +14,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'; const selector = createMemoizedSelector(selectSystemSlice, selectGallerySlice, (system, gallery) => { const imageDTO = gallery.selection[gallery.selection.length - 1]; 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 b888e8a516..4094fd1746 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,9 +1,9 @@ +import { Handle, Position } from '@xyflow/react'; import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens'; 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'; interface Props { nodeId: string; 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/InvocationNodeComponent.tsx similarity index 94% rename from invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNode.tsx rename to invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeComponent.tsx index baa7fc262a..6435e4de04 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/InvocationNodeComponent.tsx @@ -16,10 +16,10 @@ type Props = { isOpen: boolean; label: string; type: string; - selected: boolean; + selected?: boolean; }; -const InvocationNode = ({ nodeId, isOpen, label, type, selected }: Props) => { +export const InvocationNodeComponent = memo(({ nodeId, isOpen, label, type, selected = false }: Props) => { const fieldNames = useFieldNames(nodeId); const withFooter = useWithFooter(nodeId); const outputFieldNames = useOutputFieldNames(nodeId); @@ -78,6 +78,6 @@ const InvocationNode = ({ nodeId, isOpen, label, type, selected }: Props) => { )} ); -}; +}); -export default memo(InvocationNode); +InvocationNodeComponent.displayName = 'InvocationNodeComponent'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeUnknownFallback.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeUnknownFallback.tsx index cc26e672f6..621c8b8437 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeUnknownFallback.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeUnknownFallback.tsx @@ -11,10 +11,10 @@ type Props = { isOpen: boolean; label: string; type: string; - selected: boolean; + selected?: boolean; }; -const InvocationNodeUnknownFallback = ({ nodeId, isOpen, label, type, selected }: Props) => { +const InvocationNodeUnknownFallback = ({ nodeId, isOpen, label, type, selected = false }: Props) => { const { t } = useTranslation(); const nodePack = useNodePack(nodeId); return ( diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx index 1d12b6a454..cac88a3cb8 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx @@ -1,14 +1,14 @@ import { useStore } from '@nanostores/react'; +import type { NodeProps } from '@xyflow/react'; import { useAppSelector } from 'app/store/storeHooks'; -import InvocationNode from 'features/nodes/components/flow/nodes/Invocation/InvocationNode'; +import { InvocationNodeComponent } from 'features/nodes/components/flow/nodes/Invocation/InvocationNodeComponent'; import { $templates } from 'features/nodes/store/nodesSlice'; -import type { InvocationNodeData } from 'features/nodes/types/invocation'; +import type { InvocationNode } from 'features/nodes/types/invocation'; import { memo, useMemo } from 'react'; -import type { NodeProps } from 'reactflow'; import InvocationNodeUnknownFallback from './InvocationNodeUnknownFallback'; -const InvocationNodeWrapper = (props: NodeProps) => { +const InvocationNodeWrapper = (props: NodeProps) => { const { data, selected } = props; const { id: nodeId, type, isOpen, label } = data; const templates = useStore($templates); @@ -25,7 +25,7 @@ const InvocationNodeWrapper = (props: NodeProps) => { ); } - return ; + return ; }; export default memo(InvocationNodeWrapper); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldHandle.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldHandle.tsx index 143dee983f..a7cbf0f77e 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldHandle.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/FieldHandle.tsx @@ -1,4 +1,6 @@ import { Tooltip } from '@invoke-ai/ui-library'; +import type { HandleType } from '@xyflow/react'; +import { Handle, Position } from '@xyflow/react'; import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar'; import { getFieldColor } from 'features/nodes/components/flow/edges/util/getEdgeColor'; import { useFieldTypeName } from 'features/nodes/hooks/usePrettyFieldType'; @@ -8,8 +10,6 @@ import { type FieldInputTemplate, type FieldOutputTemplate, isSingle } from 'fea import type { CSSProperties } from 'react'; import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import type { HandleType } from 'reactflow'; -import { Handle, Position } from 'reactflow'; type FieldHandleProps = { fieldTemplate: FieldInputTemplate | FieldOutputTemplate; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Notes/NotesNode.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Notes/NotesNode.tsx index 76666af396..aba511ab94 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Notes/NotesNode.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Notes/NotesNode.tsx @@ -1,15 +1,15 @@ import { Box, Flex, Textarea } from '@invoke-ai/ui-library'; +import type { NodeProps } from '@xyflow/react'; import { useAppDispatch } from 'app/store/storeHooks'; import NodeCollapseButton from 'features/nodes/components/flow/nodes/common/NodeCollapseButton'; import NodeTitle from 'features/nodes/components/flow/nodes/common/NodeTitle'; import NodeWrapper from 'features/nodes/components/flow/nodes/common/NodeWrapper'; import { notesNodeValueChanged } from 'features/nodes/store/nodesSlice'; -import type { NotesNodeData } from 'features/nodes/types/invocation'; +import type { NotesNode } from 'features/nodes/types/invocation'; import type { ChangeEvent } from 'react'; import { memo, useCallback } from 'react'; -import type { NodeProps } from 'reactflow'; -const NotesNode = (props: NodeProps) => { +export const NotesNodeComponent = memo((props: NodeProps) => { const { id: nodeId, data, selected } = props; const { notes, isOpen } = data; const dispatch = useAppDispatch(); @@ -55,6 +55,6 @@ const NotesNode = (props: NodeProps) => { )} ); -}; +}); -export default memo(NotesNode); +NotesNodeComponent.displayName = 'NotesNodeComponent'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeCollapseButton.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeCollapseButton.tsx index 372854b703..b3302eb88a 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeCollapseButton.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeCollapseButton.tsx @@ -1,9 +1,9 @@ import { Icon, IconButton } from '@invoke-ai/ui-library'; +import { useUpdateNodeInternals } from '@xyflow/react'; import { useAppDispatch } from 'app/store/storeHooks'; import { nodeIsOpenChanged } from 'features/nodes/store/nodesSlice'; import { memo, useCallback } from 'react'; import { PiCaretUpBold } from 'react-icons/pi'; -import { useUpdateNodeInternals } from 'reactflow'; interface Props { nodeId: string; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx index 983aee1d48..68c945fd61 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeWrapper.tsx @@ -1,5 +1,6 @@ import type { ChakraProps } from '@invoke-ai/ui-library'; import { Box, useGlobalMenuClose, useToken } from '@invoke-ai/ui-library'; +import type { NodeChange } from '@xyflow/react'; import { useAppDispatch, useAppSelector, useAppStore } from 'app/store/storeHooks'; import NodeSelectionOverlay from 'common/components/NodeSelectionOverlay'; import { useExecutionState } from 'features/nodes/hooks/useExecutionState'; @@ -9,16 +10,15 @@ import { DRAG_HANDLE_CLASSNAME, NODE_WIDTH } from 'features/nodes/types/constant import { zNodeStatus } from 'features/nodes/types/invocation'; import type { MouseEvent, PropsWithChildren } from 'react'; import { memo, useCallback } from 'react'; -import type { NodeChange } from 'reactflow'; type NodeWrapperProps = PropsWithChildren & { nodeId: string; - selected: boolean; + selected?: boolean; width?: ChakraProps['w']; }; const NodeWrapper = (props: NodeWrapperProps) => { - const { nodeId, width, children, selected } = props; + const { nodeId, width, children, selected = false } = props; const store = useAppStore(); const { isMouseOverNode, handleMouseOut, handleMouseOver } = useMouseOverNode(nodeId); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx index 3d44e9d47b..c7296137cb 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx @@ -1,4 +1,5 @@ import { ButtonGroup, IconButton } from '@invoke-ai/ui-library'; +import { useReactFlow } from '@xyflow/react'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { shouldShowMinimapPanelChanged } from 'features/nodes/store/workflowSettingsSlice'; import { memo, useCallback } from 'react'; @@ -9,7 +10,6 @@ import { PiMagnifyingGlassPlusBold, PiMapPinBold, } from 'react-icons/pi'; -import { useReactFlow } from 'reactflow'; const ViewportControls = () => { const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx index 92668c3fa8..8be40e8bba 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx @@ -1,8 +1,8 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; import { chakra, Flex } from '@invoke-ai/ui-library'; +import { MiniMap } from '@xyflow/react'; import { useAppSelector } from 'app/store/storeHooks'; import { memo } from 'react'; -import { MiniMap } from 'reactflow'; const ChakraMiniMap = chakra(MiniMap); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx index 37fac8ee7b..aa17107756 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx @@ -16,6 +16,7 @@ import { Switch, useDisclosure, } from '@invoke-ai/ui-library'; +import { SelectionMode } from '@xyflow/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import ReloadNodeTemplatesButton from 'features/nodes/components/flow/panels/TopRightPanel/ReloadSchemaButton'; @@ -31,7 +32,6 @@ import { import type { ChangeEvent, ReactNode } from 'react'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { SelectionMode } from 'reactflow'; const formLabelProps: FormLabelProps = { flexGrow: 1 }; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/NodeEditorPanelGroup.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/NodeEditorPanelGroup.tsx index 16e007cda7..1d6d1f1b67 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/NodeEditorPanelGroup.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/NodeEditorPanelGroup.tsx @@ -1,5 +1,3 @@ -import 'reactflow/dist/style.css'; - import { Flex } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; import QueueControls from 'features/queue/components/QueueControls'; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts b/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts index 4e96c219f8..35d83a3a58 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts @@ -1,12 +1,12 @@ import { useStore } from '@nanostores/react'; +import { useReactFlow } from '@xyflow/react'; import { $templates } from 'features/nodes/store/nodesSlice'; import { NODE_WIDTH } from 'features/nodes/types/constants'; -import type { AnyNode, InvocationTemplate } from 'features/nodes/types/invocation'; +import type { AppNode, InvocationTemplate } from 'features/nodes/types/invocation'; import { buildCurrentImageNode } from 'features/nodes/util/node/buildCurrentImageNode'; import { buildInvocationNode } from 'features/nodes/util/node/buildInvocationNode'; import { buildNotesNode } from 'features/nodes/util/node/buildNotesNode'; import { useCallback } from 'react'; -import { useReactFlow } from 'reactflow'; export const useBuildNode = () => { const templates = useStore($templates); @@ -14,7 +14,7 @@ export const useBuildNode = () => { return useCallback( // string here is "any invocation type" - (type: string | 'current_image' | 'notes'): AnyNode => { + (type: string | 'current_image' | 'notes'): AppNode => { let _x = window.innerWidth / 2; let _y = window.innerHeight / 2; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useConnection.ts b/invokeai/frontend/web/src/features/nodes/hooks/useConnection.ts index 0bca73731e..597ce8ab4b 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useConnection.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useConnection.ts @@ -1,4 +1,6 @@ import { useStore } from '@nanostores/react'; +import type { EdgeChange, OnConnect, OnConnectEnd, OnConnectStart } from '@xyflow/react'; +import { useUpdateNodeInternals } from '@xyflow/react'; import { useAppStore } from 'app/store/storeHooks'; import { $mouseOverNode } from 'features/nodes/hooks/useMouseOverNode'; import { @@ -12,8 +14,6 @@ import { import { getFirstValidConnection } from 'features/nodes/store/util/getFirstValidConnection'; import { connectionToEdge } from 'features/nodes/store/util/reactFlowUtil'; import { useCallback, useMemo } from 'react'; -import type { EdgeChange, OnConnect, OnConnectEnd, OnConnectStart } from 'reactflow'; -import { useUpdateNodeInternals } from 'reactflow'; import { assert } from 'tsafe'; export const useConnection = () => { diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts b/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts index 2a2bfde904..b23cd9e3c0 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts @@ -1,3 +1,4 @@ +import type { EdgeChange, NodeChange } from '@xyflow/react'; import { getStore } from 'app/store/nanostores/store'; import { deepClone } from 'common/util/deepClone'; import { @@ -11,7 +12,6 @@ import { } from 'features/nodes/store/nodesSlice'; import { findUnoccupiedPosition } from 'features/nodes/store/util/findUnoccupiedPosition'; import { isEqual, uniqWith } from 'lodash-es'; -import type { EdgeChange, NodeChange } from 'reactflow'; import { v4 as uuidv4 } from 'uuid'; const copySelection = () => { diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts b/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts index 9a978b09a8..4d97452677 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts @@ -1,10 +1,10 @@ // TODO: enable this at some point import { useStore } from '@nanostores/react'; +import type { IsValidConnection } from '@xyflow/react'; import { useAppSelector, useAppStore } from 'app/store/storeHooks'; import { $edgePendingUpdate, $templates } from 'features/nodes/store/nodesSlice'; import { validateConnection } from 'features/nodes/store/util/validateConnection'; import { useCallback } from 'react'; -import type { Connection } from 'reactflow'; /** * NOTE: The logic here must be duplicated in `invokeai/frontend/web/src/features/nodes/store/util/makeIsConnectionValidSelector.ts` @@ -15,8 +15,8 @@ export const useIsValidConnection = () => { const store = useAppStore(); const templates = useStore($templates); const shouldValidateGraph = useAppSelector((s) => s.workflowSettings.shouldValidateGraph); - const isValidConnection = useCallback( - ({ source, sourceHandle, target, targetHandle }: Connection): boolean => { + const isValidConnection = useCallback( + ({ source, sourceHandle, target, targetHandle }) => { // Connection must have valid targets if (!(source && sourceHandle && target && targetHandle)) { return false; diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts index f9214c1572..c2239a2ede 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts @@ -1,6 +1,9 @@ import type { PayloadAction, UnknownAction } from '@reduxjs/toolkit'; import { createSlice, isAnyOf } from '@reduxjs/toolkit'; +import type { Edge, EdgeChange, NodeChange, Viewport, XYPosition } from '@xyflow/react'; +import { applyEdgeChanges, applyNodeChanges, getConnectedEdges, getIncomers, getOutgoers } from '@xyflow/react'; import type { PersistConfig, RootState } from 'app/store/store'; +import type { CollapsedEdge } from 'features/nodes/components/flow/edges/InvocationCollapsedEdge'; import { workflowLoaded } from 'features/nodes/store/actions'; import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants'; import type { @@ -46,12 +49,10 @@ import { zT2IAdapterModelFieldValue, zVAEModelFieldValue, } from 'features/nodes/types/field'; -import type { AnyNode, InvocationNodeEdge } from 'features/nodes/types/invocation'; +import type { AppNode, InvocationNodeEdge } from 'features/nodes/types/invocation'; import { isInvocationNode, isNotesNode } from 'features/nodes/types/invocation'; import { atom } from 'nanostores'; import type { MouseEvent } from 'react'; -import type { Edge, EdgeChange, NodeChange, Viewport, XYPosition } from 'reactflow'; -import { applyEdgeChanges, applyNodeChanges, getConnectedEdges, getIncomers, getOutgoers } from 'reactflow'; import type { UndoableOptions } from 'redux-undo'; import type { z } from 'zod'; @@ -92,10 +93,10 @@ export const nodesSlice = createSlice({ name: 'nodes', initialState: initialNodesState, reducers: { - nodesChanged: (state, action: PayloadAction) => { - state.nodes = applyNodeChanges(action.payload, state.nodes); + nodesChanged: (state, action: PayloadAction[]>) => { + state.nodes = applyNodeChanges(action.payload, state.nodes); // Remove edges that are no longer valid, due to a removed or otherwise changed node - const edgeChanges: EdgeChange[] = []; + const edgeChanges: EdgeChange[] = []; state.edges.forEach((e) => { const sourceExists = state.nodes.some((n) => n.id === e.source); const targetExists = state.nodes.some((n) => n.id === e.target); @@ -103,10 +104,10 @@ export const nodesSlice = createSlice({ edgeChanges.push({ type: 'remove', id: e.id }); } }); - state.edges = applyEdgeChanges(edgeChanges, state.edges); + state.edges = applyEdgeChanges(edgeChanges, state.edges); }, - edgesChanged: (state, action: PayloadAction) => { - const changes: EdgeChange[] = []; + edgesChanged: (state, action: PayloadAction[]>) => { + const changes: EdgeChange[] = []; // We may need to massage the edge changes or otherwise handle them action.payload.forEach((change) => { if (change.type === 'remove' || change.type === 'select') { @@ -134,7 +135,7 @@ export const nodesSlice = createSlice({ } changes.push(change); }); - state.edges = applyEdgeChanges(changes, state.edges); + state.edges = applyEdgeChanges(changes, state.edges); }, fieldLabelChanged: ( state, @@ -218,7 +219,7 @@ export const nodesSlice = createSlice({ (node) => isInvocationNode(node) && node.data.isOpen === false ); - const collapsedEdgesToCreate: Edge<{ count: number }>[] = []; + const collapsedEdgesToCreate: CollapsedEdge[] = []; // hide all edges connectedEdges.forEach((edge) => { @@ -238,7 +239,7 @@ export const nodesSlice = createSlice({ target: edge.target, type: 'collapsed', data: { count: 1 }, - updatable: false, + reconnectable: false, selected: edge.selected, }); } @@ -259,14 +260,14 @@ export const nodesSlice = createSlice({ target: edge.target, type: 'collapsed', data: { count: 1 }, - updatable: false, + reconnectable: false, selected: edge.selected, }); } } }); if (collapsedEdgesToCreate.length) { - state.edges = applyEdgeChanges( + state.edges = applyEdgeChanges( collapsedEdgesToCreate.map((edge) => ({ type: 'add', item: edge })), state.edges ); @@ -366,7 +367,7 @@ export const nodesSlice = createSlice({ extraReducers: (builder) => { builder.addCase(workflowLoaded, (state, action) => { const { nodes, edges } = action.payload; - state.nodes = applyNodeChanges( + state.nodes = applyNodeChanges( nodes.map((node) => ({ type: 'add', item: { ...node, ...SHARED_NODE_PROPERTIES }, @@ -416,7 +417,7 @@ export const { export const $cursorPos = atom(null); export const $templates = atom({}); -export const $copiedNodes = atom([]); +export const $copiedNodes = atom([]); export const $copiedEdges = atom([]); export const $edgesToCopiedNodes = atom([]); export const $pendingConnection = atom(null); diff --git a/invokeai/frontend/web/src/features/nodes/store/reactFlowInstance.ts b/invokeai/frontend/web/src/features/nodes/store/reactFlowInstance.ts index cfbadc6669..674df75690 100644 --- a/invokeai/frontend/web/src/features/nodes/store/reactFlowInstance.ts +++ b/invokeai/frontend/web/src/features/nodes/store/reactFlowInstance.ts @@ -1,5 +1,5 @@ +import type { ReactFlowInstance } from '@xyflow/react'; import { atom } from 'nanostores'; -import type { ReactFlowInstance } from 'reactflow'; export const $flow = atom(null); export const $needsFit = atom(true); diff --git a/invokeai/frontend/web/src/features/nodes/store/types.ts b/invokeai/frontend/web/src/features/nodes/store/types.ts index 6dcf70cfad..7c04cef164 100644 --- a/invokeai/frontend/web/src/features/nodes/store/types.ts +++ b/invokeai/frontend/web/src/features/nodes/store/types.ts @@ -1,3 +1,5 @@ +import type { HandleType } from '@xyflow/react'; +import type { CollapsedEdge } from 'features/nodes/components/flow/edges/InvocationCollapsedEdge'; import type { FieldIdentifier, FieldInputTemplate, @@ -5,13 +7,12 @@ import type { StatefulFieldValue, } from 'features/nodes/types/field'; import type { - AnyNode, + AppNode, InvocationNodeEdge, InvocationTemplate, NodeExecutionState, } from 'features/nodes/types/invocation'; import type { WorkflowV3 } from 'features/nodes/types/workflow'; -import type { HandleType } from 'reactflow'; export type Templates = Record; export type NodeExecutionStates = Record; @@ -25,8 +26,8 @@ export type PendingConnection = { export type NodesState = { _version: 1; - nodes: AnyNode[]; - edges: InvocationNodeEdge[]; + nodes: AppNode[]; + edges: (InvocationNodeEdge | CollapsedEdge)[]; }; export type WorkflowMode = 'edit' | 'view'; diff --git a/invokeai/frontend/web/src/features/nodes/store/util/findUnoccupiedPosition.ts b/invokeai/frontend/web/src/features/nodes/store/util/findUnoccupiedPosition.ts index bd110a50a1..6974a6ec54 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/findUnoccupiedPosition.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/findUnoccupiedPosition.ts @@ -1,4 +1,4 @@ -import type { Node } from 'reactflow'; +import type { Node } from '@xyflow/react'; export const findUnoccupiedPosition = (nodes: Node[], x: number, y: number) => { let newX = x; diff --git a/invokeai/frontend/web/src/features/nodes/store/util/getCollectItemType.ts b/invokeai/frontend/web/src/features/nodes/store/util/getCollectItemType.ts index e6c117d91e..063a5e5268 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/getCollectItemType.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/getCollectItemType.ts @@ -1,6 +1,6 @@ import type { Templates } from 'features/nodes/store/types'; import type { FieldType } from 'features/nodes/types/field'; -import type { AnyNode, InvocationNodeEdge } from 'features/nodes/types/invocation'; +import type { AppNode, InvocationNodeEdge } from 'features/nodes/types/invocation'; /** * Given a collect node, return the type of the items it collects. The graph is traversed to find the first node and @@ -14,7 +14,7 @@ import type { AnyNode, InvocationNodeEdge } from 'features/nodes/types/invocatio */ export const getCollectItemType = ( templates: Templates, - nodes: AnyNode[], + nodes: AppNode[], edges: InvocationNodeEdge[], nodeId: string ): FieldType | null => { diff --git a/invokeai/frontend/web/src/features/nodes/store/util/getFirstValidConnection.ts b/invokeai/frontend/web/src/features/nodes/store/util/getFirstValidConnection.ts index adc51341d7..586727234a 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/getFirstValidConnection.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/getFirstValidConnection.ts @@ -1,9 +1,9 @@ +import type { Connection, Edge } from '@xyflow/react'; import type { Templates } from 'features/nodes/store/types'; import { validateConnection } from 'features/nodes/store/util/validateConnection'; import type { FieldInputTemplate, FieldOutputTemplate } from 'features/nodes/types/field'; -import type { AnyNode, InvocationNodeEdge } from 'features/nodes/types/invocation'; +import type { AppNode, InvocationNodeEdge } from 'features/nodes/types/invocation'; import { map } from 'lodash-es'; -import type { Connection, Edge } from 'reactflow'; /** * @@ -22,7 +22,7 @@ export const getFirstValidConnection = ( sourceHandle: string | null, target: string, targetHandle: string | null, - nodes: AnyNode[], + nodes: AppNode[], edges: InvocationNodeEdge[], templates: Templates, edgePendingUpdate: Edge | null @@ -80,7 +80,7 @@ export const getTargetCandidateFields = ( source: string, sourceHandle: string, target: string, - nodes: AnyNode[], + nodes: AppNode[], edges: Edge[], templates: Templates, edgePendingUpdate: Edge | null @@ -116,7 +116,7 @@ export const getSourceCandidateFields = ( target: string, targetHandle: string, source: string, - nodes: AnyNode[], + nodes: AppNode[], edges: Edge[], templates: Templates, edgePendingUpdate: Edge | null diff --git a/invokeai/frontend/web/src/features/nodes/store/util/getHasCycles.ts b/invokeai/frontend/web/src/features/nodes/store/util/getHasCycles.ts index c1a4e51f0c..9b0b99e48d 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/getHasCycles.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/getHasCycles.ts @@ -1,5 +1,5 @@ import graphlib from '@dagrejs/graphlib'; -import type { Edge, Node } from 'reactflow'; +import type { Edge, Node } from '@xyflow/react'; /** * Check if adding an edge between the source and target nodes would create a cycle in the graph. diff --git a/invokeai/frontend/web/src/features/nodes/store/util/makeConnectionErrorSelector.ts b/invokeai/frontend/web/src/features/nodes/store/util/makeConnectionErrorSelector.ts index ec607c60c5..a15dc91a45 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/makeConnectionErrorSelector.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/makeConnectionErrorSelector.ts @@ -1,9 +1,9 @@ +import type { Edge, HandleType } from '@xyflow/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import type { RootState } from 'app/store/store'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; import type { NodesState, PendingConnection, Templates } from 'features/nodes/store/types'; import { buildRejectResult, validateConnection } from 'features/nodes/store/util/validateConnection'; -import type { Edge, HandleType } from 'reactflow'; /** * Creates a selector that validates a pending connection. diff --git a/invokeai/frontend/web/src/features/nodes/store/util/reactFlowUtil.ts b/invokeai/frontend/web/src/features/nodes/store/util/reactFlowUtil.ts index 89be7951a2..4857e9aed0 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/reactFlowUtil.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/reactFlowUtil.ts @@ -1,4 +1,4 @@ -import type { Connection, Edge } from 'reactflow'; +import type { Connection, Edge } from '@xyflow/react'; import { assert } from 'tsafe'; /** diff --git a/invokeai/frontend/web/src/features/nodes/store/util/testUtils.ts b/invokeai/frontend/web/src/features/nodes/store/util/testUtils.ts index 83988d55ea..a025f07b50 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/testUtils.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/testUtils.ts @@ -1,10 +1,9 @@ import type { Templates } from 'features/nodes/store/types'; -import type { InvocationTemplate } from 'features/nodes/types/invocation'; +import type { InvocationNodeEdge, InvocationTemplate } from 'features/nodes/types/invocation'; import { buildInvocationNode } from 'features/nodes/util/node/buildInvocationNode'; import type { OpenAPIV3_1 } from 'openapi-types'; -import type { Edge } from 'reactflow'; -export const buildEdge = (source: string, sourceHandle: string, target: string, targetHandle: string): Edge => ({ +export const buildEdge = (source: string, sourceHandle: string, target: string, targetHandle: string): InvocationNodeEdge => ({ source, sourceHandle, target, diff --git a/invokeai/frontend/web/src/features/nodes/store/util/validateConnection.ts b/invokeai/frontend/web/src/features/nodes/store/util/validateConnection.ts index 8ece852b07..cc59705cff 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/validateConnection.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/validateConnection.ts @@ -1,10 +1,10 @@ +import type { Connection as NullableConnection, Edge } from '@xyflow/react'; import type { Templates } from 'features/nodes/store/types'; import { areTypesEqual } from 'features/nodes/store/util/areTypesEqual'; import { getCollectItemType } from 'features/nodes/store/util/getCollectItemType'; import { getHasCycles } from 'features/nodes/store/util/getHasCycles'; import { validateConnectionTypes } from 'features/nodes/store/util/validateConnectionTypes'; -import type { AnyNode } from 'features/nodes/types/invocation'; -import type { Connection as NullableConnection, Edge } from 'reactflow'; +import type { AppNode, InvocationNodeEdge } from 'features/nodes/types/invocation'; import type { O } from 'ts-toolbelt'; type Connection = O.NonNullable; @@ -21,8 +21,8 @@ export type ValidationResult = type ValidateConnectionFunc = ( connection: Connection, - nodes: AnyNode[], - edges: Edge[], + nodes: AppNode[], + edges: InvocationNodeEdge[], templates: Templates, ignoreEdge: Edge | null, strict?: boolean diff --git a/invokeai/frontend/web/src/features/nodes/store/workflowSettingsSlice.ts b/invokeai/frontend/web/src/features/nodes/store/workflowSettingsSlice.ts index 4a2e45abde..663fd93da1 100644 --- a/invokeai/frontend/web/src/features/nodes/store/workflowSettingsSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/workflowSettingsSlice.ts @@ -1,7 +1,7 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import { SelectionMode } from '@xyflow/react'; import type { PersistConfig, RootState } from 'app/store/store'; -import { SelectionMode } from 'reactflow'; type WorkflowSettingsState = { _version: 1; diff --git a/invokeai/frontend/web/src/features/nodes/types/constants.ts b/invokeai/frontend/web/src/features/nodes/types/constants.ts index 05697c384c..c6cb0baa13 100644 --- a/invokeai/frontend/web/src/features/nodes/types/constants.ts +++ b/invokeai/frontend/web/src/features/nodes/types/constants.ts @@ -1,4 +1,4 @@ -import type { Node } from 'reactflow'; +import type { Node } from '@xyflow/react'; /** * How long to wait before showing a tooltip when hovering a field handle. diff --git a/invokeai/frontend/web/src/features/nodes/types/invocation.ts b/invokeai/frontend/web/src/features/nodes/types/invocation.ts index 0a7149bd6b..65aa74502e 100644 --- a/invokeai/frontend/web/src/features/nodes/types/invocation.ts +++ b/invokeai/frontend/web/src/features/nodes/types/invocation.ts @@ -1,4 +1,4 @@ -import type { Edge, Node } from 'reactflow'; +import type { Edge, Node } from '@xyflow/react'; import { z } from 'zod'; import { zClassification, zProgressImage } from './common'; @@ -59,11 +59,11 @@ type AnyNodeData = z.infer; export type InvocationNode = Node; export type NotesNode = Node; export type CurrentImageNode = Node; -export type AnyNode = Node; +export type AppNode = Node; -export const isInvocationNode = (node?: AnyNode | null): node is InvocationNode => +export const isInvocationNode = (node?: AppNode | null): node is InvocationNode => Boolean(node && node.type === 'invocation'); -export const isNotesNode = (node?: AnyNode | null): node is NotesNode => Boolean(node && node.type === 'notes'); +export const isNotesNode = (node?: AppNode | null): node is NotesNode => Boolean(node && node.type === 'notes'); export const isInvocationNodeData = (node?: AnyNodeData | null): node is InvocationNodeData => Boolean(node && !['notes', 'current_image'].includes(node.type)); // node.type may be 'notes', 'current_image', or any invocation type // #endregion diff --git a/invokeai/frontend/web/src/features/nodes/types/workflow.test-d.ts b/invokeai/frontend/web/src/features/nodes/types/workflow.test-d.ts index 7cb1ea230c..beae3a9840 100644 --- a/invokeai/frontend/web/src/features/nodes/types/workflow.test-d.ts +++ b/invokeai/frontend/web/src/features/nodes/types/workflow.test-d.ts @@ -1,5 +1,5 @@ +import type * as ReactFlow from '@xyflow/react'; import type { WorkflowCategory, WorkflowV3, XYPosition } from 'features/nodes/types/workflow'; -import type * as ReactFlow from 'reactflow'; import type { S } from 'services/api/types'; import type { Equals, Extends } from 'tsafe'; import { assert } from 'tsafe'; diff --git a/invokeai/frontend/web/src/features/nodes/util/node/buildCurrentImageNode.ts b/invokeai/frontend/web/src/features/nodes/util/node/buildCurrentImageNode.ts index d90ea31561..c7bd841673 100644 --- a/invokeai/frontend/web/src/features/nodes/util/node/buildCurrentImageNode.ts +++ b/invokeai/frontend/web/src/features/nodes/util/node/buildCurrentImageNode.ts @@ -1,6 +1,6 @@ +import type { XYPosition } from '@xyflow/react'; import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants'; import type { CurrentImageNode } from 'features/nodes/types/invocation'; -import type { XYPosition } from 'reactflow'; import { v4 as uuidv4 } from 'uuid'; export const buildCurrentImageNode = (position: XYPosition): CurrentImageNode => { diff --git a/invokeai/frontend/web/src/features/nodes/util/node/buildInvocationNode.ts b/invokeai/frontend/web/src/features/nodes/util/node/buildInvocationNode.ts index af19aa86ea..9296a1b538 100644 --- a/invokeai/frontend/web/src/features/nodes/util/node/buildInvocationNode.ts +++ b/invokeai/frontend/web/src/features/nodes/util/node/buildInvocationNode.ts @@ -1,9 +1,9 @@ +import type { XYPosition } from '@xyflow/react'; import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants'; import type { FieldInputInstance } from 'features/nodes/types/field'; import type { InvocationNode, InvocationTemplate } from 'features/nodes/types/invocation'; import { buildFieldInputInstance } from 'features/nodes/util/schema/buildFieldInputInstance'; import { reduce } from 'lodash-es'; -import type { XYPosition } from 'reactflow'; import { v4 as uuidv4 } from 'uuid'; export const buildInvocationNode = (position: XYPosition, template: InvocationTemplate): InvocationNode => { diff --git a/invokeai/frontend/web/src/features/nodes/util/node/buildNotesNode.ts b/invokeai/frontend/web/src/features/nodes/util/node/buildNotesNode.ts index c858c8854f..1a89bfc585 100644 --- a/invokeai/frontend/web/src/features/nodes/util/node/buildNotesNode.ts +++ b/invokeai/frontend/web/src/features/nodes/util/node/buildNotesNode.ts @@ -1,6 +1,6 @@ +import type { XYPosition } from '@xyflow/react'; import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants'; import type { NotesNode } from 'features/nodes/types/invocation'; -import type { XYPosition } from 'reactflow'; import { v4 as uuidv4 } from 'uuid'; export const buildNotesNode = (position: XYPosition): NotesNode => { diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx index 256a4331cd..7ec67f5254 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx @@ -1,10 +1,10 @@ import { Box } from '@invoke-ai/ui-library'; +import { ReactFlowProvider } from '@xyflow/react'; import { useAppSelector } from 'app/store/storeHooks'; import { ImageComparisonDroppable } from 'features/gallery/components/ImageViewer/ImageComparisonDroppable'; import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer'; import NodeEditor from 'features/nodes/components/NodeEditor'; import { memo } from 'react'; -import { ReactFlowProvider } from 'reactflow'; const NodesTab = () => { const mode = useAppSelector((s) => s.workflow.mode);