Revert "rough rough POC of video tab"

This reverts commit e89266bfe3.
This commit is contained in:
Mary Hipp Rogers
2025-08-28 08:26:09 -04:00
parent e600cdc826
commit ca40daeb97
27 changed files with 7 additions and 1278 deletions

View File

@@ -87,7 +87,6 @@
"react-hotkeys-hook": "4.5.0",
"react-i18next": "^15.5.3",
"react-icons": "^5.5.0",
"react-player": "^3.3.1",
"react-redux": "9.2.0",
"react-resizable-panels": "^3.0.3",
"react-textarea-autosize": "^8.5.9",

View File

@@ -152,9 +152,6 @@ importers:
react-icons:
specifier: ^5.5.0
version: 5.5.0(react@18.3.1)
react-player:
specifier: ^3.3.1
version: 3.3.1(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-redux:
specifier: 9.2.0
version: 9.2.0(@types/react@18.3.23)(react@18.3.1)(redux@5.0.1)
@@ -940,31 +937,6 @@ packages:
'@microsoft/tsdoc@0.15.1':
resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==}
'@mux/mux-data-google-ima@0.2.8':
resolution: {integrity: sha512-0ZEkHdcZ6bS8QtcjFcoJeZxJTpX7qRIledf4q1trMWPznugvtajCjCM2kieK/pzkZj1JM6liDRFs1PJSfVUs2A==}
'@mux/mux-player-react@3.5.3':
resolution: {integrity: sha512-f0McZbIXYDkzecFwhhkf0JgEInPnsOClgBqBhkdhRlLRdrAzMATib+D3Di3rPkRHNH7rc/WWORvSxgJz6m6zkA==}
peerDependencies:
'@types/react': ^17.0.0 || ^17.0.0-0 || ^18 || ^18.0.0-0 || ^19 || ^19.0.0-0
'@types/react-dom': '*'
react: ^17.0.2 || ^17.0.0-0 || ^18 || ^18.0.0-0 || ^19 || ^19.0.0-0
react-dom: ^17.0.2 || ^17.0.2-0 || ^18 || ^18.0.0-0 || ^19 || ^19.0.0-0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@mux/mux-player@3.5.3':
resolution: {integrity: sha512-uXKFXbdtioAi+clSVfD60Rw4r7OvA62u2jV6aar9loW9qMsmKv8LU+8uaIaWQjyAORp6E0S37GOVjo72T6O2eQ==}
'@mux/mux-video@0.26.1':
resolution: {integrity: sha512-gkMdBAgNlB4+krANZHkQFzYWjWeNsJz69y1/hnPtmNQnpvW+O7oc71OffcZrbblyibSxWMQ6MQpYmBVjXlp6sA==}
'@mux/playback-core@0.30.1':
resolution: {integrity: sha512-rnO1NE9xHDyzbAkmE6ygJYcD7cyyMt7xXqWTykxlceaoSXLjUqgp42HDio7Lcidto4x/O4FIa7ztjV2aCBCXgQ==}
'@nanostores/react@0.7.3':
resolution: {integrity: sha512-/XuLAMENRu/Q71biW4AZ4qmU070vkZgiQ28gaTSNRPm2SZF5zGAR81zPE1MaMB4SeOp6ZTst92NBaG75XSspNg==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -1481,9 +1453,6 @@ packages:
typescript:
optional: true
'@svta/common-media-library@0.12.4':
resolution: {integrity: sha512-9EuOoaNmz7JrfGwjsrD9SxF9otU5TNMnbLu1yU4BeLK0W5cDxVXXR58Z89q9u2AnHjIctscjMTYdlqQ1gojTuw==}
'@swc/core-darwin-arm64@1.12.9':
resolution: {integrity: sha512-GACFEp4nD6V+TZNR2JwbMZRHB+Yyvp14FrcmB6UCUYmhuNWjkxi+CLnEvdbuiKyQYv0zA+TRpCHZ+whEs6gwfA==}
engines: {node: '>=10'}
@@ -1738,12 +1707,6 @@ packages:
resolution: {integrity: sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@vercel/edge@1.2.2':
resolution: {integrity: sha512-1+y+f6rk0Yc9ss9bRDgz/gdpLimwoRteKHhrcgHvEpjbP1nyT3ByqEMWm2BTcpIO5UtDmIFXc8zdq4LR190PDA==}
'@vimeo/player@2.29.0':
resolution: {integrity: sha512-9JjvjeqUndb9otCCFd0/+2ESsLk7VkDE6sxOBy9iy2ukezuQbplVRi+g9g59yAurKofbmTi/KcKxBGO/22zWRw==}
'@vitejs/plugin-react-swc@3.10.2':
resolution: {integrity: sha512-xD3Rdvrt5LgANug7WekBn1KhcvLn1H3jNBfJRL3reeOIua/WnZOEV5qi5qIBq5T8R0jUDmRtxuvk4bPhzGHDWw==}
peerDependencies:
@@ -1996,15 +1959,6 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
bcp-47-match@2.0.3:
resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==}
bcp-47-normalize@2.3.0:
resolution: {integrity: sha512-8I/wfzqQvttUFz7HVJgIZ7+dj3vUaIyIxYXaTRP1YWoSDfzt6TUmxaKZeuXR62qBmYr+nvuWINFRl6pZ5DlN4Q==}
bcp-47@2.1.0:
resolution: {integrity: sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==}
better-opn@3.0.2:
resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==}
engines: {node: '>=12.0.0'}
@@ -2060,14 +2014,6 @@ packages:
caniuse-lite@1.0.30001727:
resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==}
castable-video@1.1.10:
resolution: {integrity: sha512-/T1I0A4VG769wTEZ8gWuy1Crn9saAfRTd1UYTb8xbOPlN78+zOi/1nU2dD5koNkfE5VWvgabkIqrGKmyNXOjSQ==}
ce-la-react@0.3.1:
resolution: {integrity: sha512-g0YwpZDPIwTwFumGTzNHcgJA6VhFfFCJkSNdUdC04br2UfU+56JDrJrJva3FZ7MToB4NDHAFBiPE/PZdNl1mQA==}
peerDependencies:
react: '>=17.0.0'
chai@5.2.0:
resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
engines: {node: '>=12'}
@@ -2118,18 +2064,12 @@ packages:
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
engines: {node: '>=0.8'}
cloudflare-video-element@1.3.3:
resolution: {integrity: sha512-qrHzwLmUhisoIuEoKc7iBbdzBNj2Pi7ThHslU/9U/6PY9DEvo4mh/U+w7OVuzXT9ks7ZXfARvDBfPAaMGF/hIg==}
cmdk@1.1.1:
resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==}
peerDependencies:
react: ^18 || ^19 || ^19.0.0-rc
react-dom: ^18 || ^19 || ^19.0.0-rc
codem-isoboxer@0.3.10:
resolution: {integrity: sha512-eNk3TRV+xQMJ1PEj0FQGY8KD4m0GPxT487XJ+Iftm7mVa9WpPFDMWqPt+46buiP5j5Wzqe5oMIhqBcAeKfygSA==}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@@ -2199,9 +2139,6 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
custom-media-element@1.4.5:
resolution: {integrity: sha512-cjrsQufETwxjvwZbYbKBCJNvmQ2++G9AvT45zDi7NXL9k2PdVcs2h0jQz96J6G4TMKRCcEsoJ+QTgQD00Igtjw==}
d3-color@3.1.0:
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
engines: {node: '>=12'}
@@ -2240,12 +2177,6 @@ packages:
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
engines: {node: '>=12'}
dash-video-element@0.1.6:
resolution: {integrity: sha512-4gHShaQjcFv6diX5EzB6qAdUGKlIUGGZY8J8yp2pQkWqR0jX4c6plYy0cFraN7mr0DZINe8ujDN1fssDYxJjcg==}
dashjs@5.0.3:
resolution: {integrity: sha512-TXndNnCUjFjF2nYBxDVba+hWRpVkadkQ8flLp7kHkem+5+wZTfRShJCnVkPUosmjS0YPE9fVNLbYPJxHBeQZvA==}
data-view-buffer@1.0.2:
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
engines: {node: '>= 0.4'}
@@ -2820,18 +2751,9 @@ packages:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
hls-video-element@1.5.6:
resolution: {integrity: sha512-KPdvSR+oBJPiCVb+m6pd2mn3rJEjNbaK8pGhSkxFI2pmyvZIeTVQrPbEO9PT/juwXHwhvCoKJnNxAuFwJG9H5A==}
hls.js@1.6.9:
resolution: {integrity: sha512-q7qPrri6GRwjcNd7EkFCmhiJ6PBIxeUsdxKbquBkQZpg9jAnp6zSAeN9eEWFlOB09J8JfzAQGoXL5ZEAltjO9g==}
hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
html-entities@2.6.0:
resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==}
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
@@ -2870,9 +2792,6 @@ packages:
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
engines: {node: '>= 4'}
immediate@3.0.6:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
immer@10.1.1:
resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==}
@@ -2884,9 +2803,6 @@ packages:
resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
engines: {node: '>=8'}
imsc@1.1.5:
resolution: {integrity: sha512-V8je+CGkcvGhgl2C1GlhqFFiUOIEdwXbXLiu1Fcubvvbo+g9inauqT3l0pNYXGoLPBj3jxtZz9t+wCopMkwadQ==}
imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
@@ -2909,12 +2825,6 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
is-alphabetical@2.0.1:
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
is-alphanumerical@2.0.1:
resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
is-array-buffer@3.0.5:
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
engines: {node: '>= 0.4'}
@@ -2950,9 +2860,6 @@ packages:
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
engines: {node: '>= 0.4'}
is-decimal@2.0.1:
resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
is-docker@2.2.1:
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
engines: {node: '>=8'}
@@ -3157,9 +3064,6 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
lie@3.1.1:
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
@@ -3184,9 +3088,6 @@ packages:
resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
engines: {node: '>=14'}
localforage@1.10.0:
resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==}
locate-path@6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
@@ -3253,12 +3154,6 @@ packages:
mdn-data@2.0.14:
resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
media-chrome@4.11.1:
resolution: {integrity: sha512-+2niDc4qOwlpFAjwxg1OaizK/zKV6y7QqGm4nBFEVlSaG0ZBgOmfc4IXAPiirZqAlZGaFFUaMqCl1SpGU0/naA==}
media-tracks@0.3.3:
resolution: {integrity: sha512-9P2FuUHnZZ3iji+2RQk7Zkh5AmZTnOG5fODACnjhCVveX1McY3jmCRHofIEI+yTBqplz7LXy48c7fQ3Uigp88w==}
memoize-one@6.0.0:
resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
@@ -3324,12 +3219,6 @@ packages:
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
mux-embed@5.11.0:
resolution: {integrity: sha512-uczzXVraqMRmyYmpGh2zthTmBKvvc5D5yaVKQRgGhFOnF7E4nkhqNkdkQc4C0WTPzdqdPl5OtCelNWMF4tg5RQ==}
mux-embed@5.9.0:
resolution: {integrity: sha512-wmunL3uoPhma/tWy8PrDPZkvJpXvSFBwbD3KkC4PG8Ztjfb1X3hRJwGUAQyRz7z99b/ovLm2UTTitrkvStjH4w==}
nano-css@5.6.2:
resolution: {integrity: sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw==}
peerDependencies:
@@ -3354,9 +3243,6 @@ packages:
resolution: {integrity: sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw==}
engines: {node: ^20.0.0 || >=22.0.0}
native-promise-only@0.8.1:
resolution: {integrity: sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg==}
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
@@ -3547,9 +3433,6 @@ packages:
pkg-types@2.2.0:
resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==}
player.style@0.1.9:
resolution: {integrity: sha512-aFmIhHMrnAP8YliFYFMnRw+5AlHqBvnqWy4vHGo2kFxlC+XjmTXqgg62qSxlE8ubAY83c0ViEZGYglSJi6mGCA==}
pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
@@ -3692,13 +3575,6 @@ packages:
react-is@17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
react-player@3.3.1:
resolution: {integrity: sha512-wE/xLloneXZ1keelFCaNeIFVNUp4/7YoUjfHjwF945aQzsbDKiIB0LQuCchGL+la0Y1IybxnR0R6Cm3AiqInMw==}
peerDependencies:
'@types/react': ^17.0.0 || ^18 || ^19
react: ^17.0.2 || ^18 || ^19
react-dom: ^17.0.2 || ^18 || ^19
react-redux@9.2.0:
resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==}
peerDependencies:
@@ -3933,9 +3809,6 @@ packages:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
sax@1.2.1:
resolution: {integrity: sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==}
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
@@ -4058,9 +3931,6 @@ packages:
resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==}
engines: {node: '>=12'}
spotify-audio-element@1.0.2:
resolution: {integrity: sha512-YEovyyeJTJMzdSVqFw/Fx19e1gdcD4bmZZ/fWS0Ji58KTpvAT2rophgK87ocqpy6eJNSmIHikhgbRjGWumgZew==}
sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
@@ -4169,9 +4039,6 @@ packages:
stylis@4.3.6:
resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
super-media-element@1.4.2:
resolution: {integrity: sha512-9pP/CVNp4NF2MNlRzLwQkjiTgKKe9WYXrLh9+8QokWmMxz+zt2mf1utkWLco26IuA3AfVcTb//qtlTIjY3VHxA==}
supports-color@10.0.0:
resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==}
engines: {node: '>=18'}
@@ -4196,9 +4063,6 @@ packages:
resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==}
engines: {node: '>=10'}
tiktok-video-element@0.1.0:
resolution: {integrity: sha512-PVWUlpDdQ/LPXi7x4/furfD7Xh1L72CgkGCaMsZBIjvxucMGm1DDPJdM9IhWBFfo6tuR4cYVO/v596r6GG/lvQ==}
tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
@@ -4284,9 +4148,6 @@ packages:
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
twitch-video-element@0.1.2:
resolution: {integrity: sha512-/up4KiWiTYiav+CUo+/DbV8JhP4COwEKSo8h1H/Zft/5NzZ/ZiIQ48h7erFKvwzalN0GfkEGGIfwIzAO0h7FHQ==}
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@@ -4321,10 +4182,6 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
ua-parser-js@1.0.40:
resolution: {integrity: sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==}
hasBin: true
ufo@1.6.1:
resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
@@ -4429,9 +4286,6 @@ packages:
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
hasBin: true
vimeo-video-element@1.5.3:
resolution: {integrity: sha512-OQWyGS9nTouMqfRJyvmAm/n6IRhZ7x3EfPAef+Q+inGBeHa3SylDbtyeB/rEBd4B/T/lcYBW3rjaD9W2DRYkiQ==}
vite-node@3.2.4:
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -4547,10 +4401,6 @@ packages:
wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
weakmap-polyfill@2.0.4:
resolution: {integrity: sha512-ZzxBf288iALJseijWelmECm/1x7ZwQn3sMYIkDr2VvZp7r6SEKuT8D0O9Wiq6L9Nl5mazrOMcmiZE/2NCenaxw==}
engines: {node: '>=8.10.0'}
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@@ -4586,9 +4436,6 @@ packages:
engines: {node: '>=8'}
hasBin: true
wistia-video-element@1.3.3:
resolution: {integrity: sha512-ZVC8HH8uV3mQGcSz10MACLDalao/0YdVverNN4GNFsOXiumfqSiZnRVc8WZEywgVckBkR7+yerQYESYPDzvTfQ==}
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
@@ -4662,9 +4509,6 @@ packages:
resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==}
engines: {node: '>=12.20'}
youtube-video-element@1.6.1:
resolution: {integrity: sha512-FDRgXlPxpe1bh6HlhL6GfJVcvVNaZKCcLEZ90X1G3Iu+z2g2cIhm2OWj9abPZq1Zqit6SY7Gwh13H9g7acoBnQ==}
zod-validation-error@3.5.3:
resolution: {integrity: sha512-OT5Y8lbUadqVZCsnyFaTQ4/O2mys4tj7PqhdbBCp7McPwvIEKfPtdA6QfPeFQK2/Rz5LgwmAXRJTugBNBi0btw==}
engines: {node: '>=18.0.0'}
@@ -5391,43 +5235,6 @@ snapshots:
'@microsoft/tsdoc@0.15.1': {}
'@mux/mux-data-google-ima@0.2.8':
dependencies:
mux-embed: 5.9.0
'@mux/mux-player-react@3.5.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@mux/mux-player': 3.5.3(react@18.3.1)
'@mux/playback-core': 0.30.1
prop-types: 15.8.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.23
'@types/react-dom': 18.3.7(@types/react@18.3.23)
'@mux/mux-player@3.5.3(react@18.3.1)':
dependencies:
'@mux/mux-video': 0.26.1
'@mux/playback-core': 0.30.1
media-chrome: 4.11.1(react@18.3.1)
player.style: 0.1.9(react@18.3.1)
transitivePeerDependencies:
- react
'@mux/mux-video@0.26.1':
dependencies:
'@mux/mux-data-google-ima': 0.2.8
'@mux/playback-core': 0.30.1
castable-video: 1.1.10
custom-media-element: 1.4.5
media-tracks: 0.3.3
'@mux/playback-core@0.30.1':
dependencies:
hls.js: 1.6.9
mux-embed: 5.11.0
'@nanostores/react@0.7.3(nanostores@0.11.4)(react@18.3.1)':
dependencies:
nanostores: 0.11.4
@@ -5883,8 +5690,6 @@ snapshots:
optionalDependencies:
typescript: 5.8.3
'@svta/common-media-library@0.12.4': {}
'@swc/core-darwin-arm64@1.12.9':
optional: true
@@ -6166,13 +5971,6 @@ snapshots:
'@typescript-eslint/types': 8.37.0
eslint-visitor-keys: 4.2.1
'@vercel/edge@1.2.2': {}
'@vimeo/player@2.29.0':
dependencies:
native-promise-only: 0.8.1
weakmap-polyfill: 2.0.4
'@vitejs/plugin-react-swc@3.10.2(vite@7.0.5(@types/node@22.16.0)(jiti@2.4.2))':
dependencies:
'@rolldown/pluginutils': 1.0.0-beta.11
@@ -6506,19 +6304,6 @@ snapshots:
base64-js@1.5.1: {}
bcp-47-match@2.0.3: {}
bcp-47-normalize@2.3.0:
dependencies:
bcp-47: 2.1.0
bcp-47-match: 2.0.3
bcp-47@2.1.0:
dependencies:
is-alphabetical: 2.0.1
is-alphanumerical: 2.0.1
is-decimal: 2.0.1
better-opn@3.0.2:
dependencies:
open: 8.4.2
@@ -6581,14 +6366,6 @@ snapshots:
caniuse-lite@1.0.30001727: {}
castable-video@1.1.10:
dependencies:
custom-media-element: 1.4.5
ce-la-react@0.3.1(react@18.3.1):
dependencies:
react: 18.3.1
chai@5.2.0:
dependencies:
assertion-error: 2.0.1
@@ -6646,8 +6423,6 @@ snapshots:
clone@1.0.4: {}
cloudflare-video-element@1.3.3: {}
cmdk@1.1.1(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1)
@@ -6660,8 +6435,6 @@ snapshots:
- '@types/react'
- '@types/react-dom'
codem-isoboxer@0.3.10: {}
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@@ -6737,8 +6510,6 @@ snapshots:
csstype@3.1.3: {}
custom-media-element@1.4.5: {}
d3-color@3.1.0: {}
d3-dispatch@3.0.1: {}
@@ -6775,24 +6546,6 @@ snapshots:
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
dash-video-element@0.1.6:
dependencies:
custom-media-element: 1.4.5
dashjs: 5.0.3
dashjs@5.0.3:
dependencies:
'@svta/common-media-library': 0.12.4
bcp-47-match: 2.0.3
bcp-47-normalize: 2.3.0
codem-isoboxer: 0.3.10
fast-deep-equal: 3.1.3
html-entities: 2.6.0
imsc: 1.1.5
localforage: 1.10.0
path-browserify: 1.0.1
ua-parser-js: 1.0.40
data-view-buffer@1.0.2:
dependencies:
call-bound: 1.0.4
@@ -7491,20 +7244,10 @@ snapshots:
he@1.2.0: {}
hls-video-element@1.5.6:
dependencies:
custom-media-element: 1.4.5
hls.js: 1.6.9
media-tracks: 0.3.3
hls.js@1.6.9: {}
hoist-non-react-statics@3.3.2:
dependencies:
react-is: 16.13.1
html-entities@2.6.0: {}
html-escaper@2.0.2: {}
html-parse-stringify@3.0.1:
@@ -7540,8 +7283,6 @@ snapshots:
ignore@7.0.5: {}
immediate@3.0.6: {}
immer@10.1.1: {}
import-fresh@3.3.1:
@@ -7551,10 +7292,6 @@ snapshots:
import-lazy@4.0.0: {}
imsc@1.1.5:
dependencies:
sax: 1.2.1
imurmurhash@0.1.4: {}
indent-string@4.0.0: {}
@@ -7573,13 +7310,6 @@ snapshots:
hasown: 2.0.2
side-channel: 1.1.0
is-alphabetical@2.0.1: {}
is-alphanumerical@2.0.1:
dependencies:
is-alphabetical: 2.0.1
is-decimal: 2.0.1
is-array-buffer@3.0.5:
dependencies:
call-bind: 1.0.8
@@ -7622,8 +7352,6 @@ snapshots:
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-decimal@2.0.1: {}
is-docker@2.2.1: {}
is-extglob@2.1.1: {}
@@ -7825,10 +7553,6 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
lie@3.1.1:
dependencies:
immediate: 3.0.6
lines-and-columns@1.2.4: {}
linkify-react@4.3.1(linkifyjs@4.3.1)(react@18.3.1):
@@ -7851,10 +7575,6 @@ snapshots:
pkg-types: 2.2.0
quansync: 0.2.10
localforage@1.10.0:
dependencies:
lie: 3.1.1
locate-path@6.0.0:
dependencies:
p-locate: 5.0.0
@@ -7914,15 +7634,6 @@ snapshots:
mdn-data@2.0.14: {}
media-chrome@4.11.1(react@18.3.1):
dependencies:
'@vercel/edge': 1.2.2
ce-la-react: 0.3.1(react@18.3.1)
transitivePeerDependencies:
- react
media-tracks@0.3.3: {}
memoize-one@6.0.0: {}
merge2@1.4.1: {}
@@ -7979,10 +7690,6 @@ snapshots:
muggle-string@0.4.1: {}
mux-embed@5.11.0: {}
mux-embed@5.9.0: {}
nano-css@5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@jridgewell/sourcemap-codec': 1.5.4
@@ -8004,8 +7711,6 @@ snapshots:
nanostores@1.0.1: {}
native-promise-only@0.8.1: {}
natural-compare@1.4.0: {}
nearley@2.20.1:
@@ -8224,12 +7929,6 @@ snapshots:
exsolve: 1.0.7
pathe: 2.0.3
player.style@0.1.9(react@18.3.1):
dependencies:
media-chrome: 4.11.1(react@18.3.1)
transitivePeerDependencies:
- react
pluralize@8.0.0: {}
possible-typed-array-names@1.1.0: {}
@@ -8367,24 +8066,6 @@ snapshots:
react-is@17.0.2: {}
react-player@3.3.1(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@mux/mux-player-react': 3.5.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@types/react': 18.3.23
cloudflare-video-element: 1.3.3
dash-video-element: 0.1.6
hls-video-element: 1.5.6
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
spotify-audio-element: 1.0.2
tiktok-video-element: 0.1.0
twitch-video-element: 0.1.2
vimeo-video-element: 1.5.3
wistia-video-element: 1.3.3
youtube-video-element: 1.6.1
transitivePeerDependencies:
- '@types/react-dom'
react-redux@9.2.0(@types/react@18.3.23)(react@18.3.1)(redux@5.0.1):
dependencies:
'@types/use-sync-external-store': 0.0.6
@@ -8679,8 +8360,6 @@ snapshots:
safe-stable-stringify@2.5.0: {}
sax@1.2.1: {}
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
@@ -8805,8 +8484,6 @@ snapshots:
split-on-first@3.0.0: {}
spotify-audio-element@1.0.2: {}
sprintf-js@1.0.3: {}
stable-hash@0.0.6: {}
@@ -8950,8 +8627,6 @@ snapshots:
stylis@4.3.6: {}
super-media-element@1.4.2: {}
supports-color@10.0.0: {}
supports-color@7.2.0:
@@ -8972,8 +8647,6 @@ snapshots:
throttle-debounce@3.0.1: {}
tiktok-video-element@0.1.0: {}
tiny-invariant@1.3.3: {}
tinybench@2.9.0: {}
@@ -9036,8 +8709,6 @@ snapshots:
tslib@2.8.1: {}
twitch-video-element@0.1.2: {}
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
@@ -9081,8 +8752,6 @@ snapshots:
typescript@5.8.3: {}
ua-parser-js@1.0.40: {}
ufo@1.6.1: {}
unbox-primitive@1.1.0:
@@ -9165,10 +8834,6 @@ snapshots:
uuid@11.1.0: {}
vimeo-video-element@1.5.3:
dependencies:
'@vimeo/player': 2.29.0
vite-node@3.2.4(@types/node@22.16.0)(jiti@2.4.2):
dependencies:
cac: 6.7.14
@@ -9297,8 +8962,6 @@ snapshots:
dependencies:
defaults: 1.0.4
weakmap-polyfill@2.0.4: {}
webidl-conversions@3.0.1: {}
webpack-virtual-modules@0.6.2: {}
@@ -9358,10 +9021,6 @@ snapshots:
siginfo: 2.0.0
stackback: 0.0.2
wistia-video-element@1.3.3:
dependencies:
super-media-element: 1.4.2
word-wrap@1.2.5: {}
wrap-ansi@7.0.0:
@@ -9408,8 +9067,6 @@ snapshots:
yocto-queue@1.2.1: {}
youtube-video-element@1.6.1: {}
zod-validation-error@3.5.3(zod@3.25.76):
dependencies:
zod: 3.25.76

View File

@@ -1251,6 +1251,7 @@
"modelIncompatibleScaledBboxWidth": "Scaled bbox width is {{width}} but {{model}} requires multiple of {{multiple}}",
"modelIncompatibleScaledBboxHeight": "Scaled bbox height is {{height}} but {{model}} requires multiple of {{multiple}}",
"fluxModelMultipleControlLoRAs": "Can only use 1 Control LoRA at a time",
"fluxKontextMultipleReferenceImages": "Can only use 1 Reference Image at a time with FLUX Kontext via BFL API",
"canvasIsFiltering": "Canvas is busy (filtering)",
"canvasIsTransforming": "Canvas is busy (transforming)",
"canvasIsRasterizing": "Canvas is busy (rasterizing)",
@@ -1281,7 +1282,6 @@
"imageActions": "Image Actions",
"sendToCanvas": "Send To Canvas",
"sendToUpscale": "Send To Upscale",
"sendToVideo": "Send To Video",
"showOptionsPanel": "Show Side Panel (O or T)",
"shuffle": "Shuffle Seed",
"steps": "Steps",
@@ -2565,15 +2565,13 @@
"queue": "Queue",
"upscaling": "Upscaling",
"upscalingTab": "$t(ui.tabs.upscaling) $t(common.tab)",
"video": "Video",
"gallery": "Gallery"
},
"panels": {
"launchpad": "Launchpad",
"workflowEditor": "Workflow Editor",
"imageViewer": "Image Viewer",
"canvas": "Canvas",
"video": "Video"
"canvas": "Canvas"
},
"launchpad": {
"workflowsTitle": "Go deep with Workflows.",
@@ -2654,10 +2652,6 @@
}
}
},
"video": {
"noVideoSelected": "No video selected",
"selectFromGallery": "Select a video from the gallery to play"
},
"system": {
"enableLogging": "Enable Logging",
"logLevel": {

View File

@@ -54,7 +54,6 @@ import { actionsDenylist } from './middleware/devtools/actionsDenylist';
import { stateSanitizer } from './middleware/devtools/stateSanitizer';
import { addArchivedOrDeletedBoardListener } from './middleware/listenerMiddleware/listeners/addArchivedOrDeletedBoardListener';
import { addImageUploadedFulfilledListener } from './middleware/listenerMiddleware/listeners/imageUploaded';
import { videoSliceConfig } from 'features/parameters/store/videoSlice';
export const listenerMiddleware = createListenerMiddleware();
@@ -79,7 +78,6 @@ const SLICE_CONFIGS = {
[systemSliceConfig.slice.reducerPath]: systemSliceConfig,
[uiSliceConfig.slice.reducerPath]: uiSliceConfig,
[upscaleSliceConfig.slice.reducerPath]: upscaleSliceConfig,
[videoSliceConfig.slice.reducerPath]: videoSliceConfig,
[workflowLibrarySliceConfig.slice.reducerPath]: workflowLibrarySliceConfig,
[workflowSettingsSliceConfig.slice.reducerPath]: workflowSettingsSliceConfig,
};
@@ -113,7 +111,6 @@ const ALL_REDUCERS = {
[systemSliceConfig.slice.reducerPath]: systemSliceConfig.slice.reducer,
[uiSliceConfig.slice.reducerPath]: uiSliceConfig.slice.reducer,
[upscaleSliceConfig.slice.reducerPath]: upscaleSliceConfig.slice.reducer,
[videoSliceConfig.slice.reducerPath]: videoSliceConfig.slice.reducer,
[workflowLibrarySliceConfig.slice.reducerPath]: workflowLibrarySliceConfig.slice.reducer,
[workflowSettingsSliceConfig.slice.reducerPath]: workflowSettingsSliceConfig.slice.reducer,
};

View File

@@ -37,7 +37,6 @@ const REGION_NAMES = [
'workflows',
'progress',
'settings',
'video',
] as const;
/**
* The names of the focus regions.

View File

@@ -122,7 +122,6 @@ export const useGlobalHotkeys = () => {
dependencies: [dispatch, isModelManagerEnabled],
});
const deleteImageModalApi = useDeleteImageModalApi();
useRegisteredHotkeys({
id: 'deleteSelection',

View File

@@ -1,31 +0,0 @@
import { MenuItem } from '@invoke-ai/ui-library';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { navigationApi } from 'features/ui/layouts/navigation-api';
import { setCurrentVideo } from 'features/ui/layouts/video-store';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiVideoBold } from 'react-icons/pi';
export const ImageMenuItemSendToVideo = memo(() => {
const { t } = useTranslation();
const imageDTO = useImageDTOContext();
const onClick = useCallback(() => {
// For now, we'll use the image URL as a video source
// In a real implementation, you might want to convert the image to video or use a different approach
setCurrentVideo(imageDTO.image_url);
navigationApi.switchToTab('video');
}, [imageDTO.image_url]);
return (
<MenuItem
icon={<PiVideoBold />}
onClickCapture={onClick}
aria-label={"Send to Video"}
>
Send to Video
</MenuItem>
);
});
ImageMenuItemSendToVideo.displayName = 'ImageMenuItemSendToVideo';

View File

@@ -14,16 +14,16 @@ import { ImageMenuItemOpenInNewTab } from 'features/gallery/components/ImageCont
import { ImageMenuItemOpenInViewer } from 'features/gallery/components/ImageContextMenu/ImageMenuItemOpenInViewer';
import { ImageMenuItemSelectForCompare } from 'features/gallery/components/ImageContextMenu/ImageMenuItemSelectForCompare';
import { ImageMenuItemSendToUpscale } from 'features/gallery/components/ImageContextMenu/ImageMenuItemSendToUpscale';
import { ImageMenuItemSendToVideo } from 'features/gallery/components/ImageContextMenu/ImageMenuItemSendToVideo';
import { ImageMenuItemStarUnstar } from 'features/gallery/components/ImageContextMenu/ImageMenuItemStarUnstar';
import { ImageMenuItemUseAsPromptTemplate } from 'features/gallery/components/ImageContextMenu/ImageMenuItemUseAsPromptTemplate';
import { ImageMenuItemUseAsRefImage } from 'features/gallery/components/ImageContextMenu/ImageMenuItemUseAsRefImage';
import { ImageMenuItemUseForPromptGeneration } from 'features/gallery/components/ImageContextMenu/ImageMenuItemUseForPromptGeneration';
import { ImageDTOContextProvider } from 'features/gallery/contexts/ImageDTOContext';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo } from 'react';
import type { ImageDTO } from 'services/api/types';
import { ImageMenuItemMetadataRecallActionsUpscaleTab } from './ImageMenuItemMetadataRecallActionsUpscaleTab';
import { ImageMenuItemUseAsPromptTemplate } from './ImageMenuItemUseAsPromptTemplate';
type SingleSelectionMenuItemsProps = {
imageDTO: ImageDTO;
@@ -48,7 +48,6 @@ const SingleSelectionMenuItems = ({ imageDTO }: SingleSelectionMenuItemsProps) =
{tab === 'upscaling' && <ImageMenuItemMetadataRecallActionsUpscaleTab />}
<MenuDivider />
<ImageMenuItemSendToUpscale />
<ImageMenuItemSendToVideo />
<ImageMenuItemUseForPromptGeneration />
{(tab === 'canvas' || tab === 'generate') && <ImageMenuItemUseAsRefImage />}
<ImageMenuItemUseAsPromptTemplate />
@@ -66,4 +65,4 @@ const SingleSelectionMenuItems = ({ imageDTO }: SingleSelectionMenuItemsProps) =
);
};
export default SingleSelectionMenuItems;
export default memo(SingleSelectionMenuItems);

View File

@@ -1,118 +0,0 @@
import { logger } from 'app/logging/logger';
import type { RootState } from 'app/store/store';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
import { selectVideoFirstFrameImage, selectVideoLastFrameImage } from 'features/parameters/store/videoSlice';
import { zImageField } from 'features/nodes/types/common';
import { Graph } from 'features/nodes/util/graph/generation/Graph';
import { selectPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils';
import type { GraphBuilderArg, GraphBuilderReturn } from 'features/nodes/util/graph/types';
import { UnsupportedGenerationModeError } from 'features/nodes/util/graph/types';
import { t } from 'i18next';
import { assert } from 'tsafe';
const log = logger('system');
// Default video parameters - these could be moved to a video params slice in the future
const DEFAULT_VIDEO_DURATION = 5;
const DEFAULT_VIDEO_ASPECT_RATIO = "1280:768"; // Default landscape
const DEFAULT_ENHANCE_PROMPT = true;
// Video parameter extraction helper
const getVideoParameters = (state: RootState) => {
// In the future, these could come from a dedicated video parameters slice
// For now, we use defaults but allow them to be overridden by any video-specific state
return {
duration: DEFAULT_VIDEO_DURATION,
aspectRatio: DEFAULT_VIDEO_ASPECT_RATIO,
enhancePrompt: DEFAULT_ENHANCE_PROMPT,
};
};
export const buildRunwayVideoGraph = (arg: GraphBuilderArg): GraphBuilderReturn => {
const { generationMode, state, manager } = arg;
log.debug({ generationMode, manager: manager?.id }, 'Building Runway video graph');
// Runway video generation supports text-to-video and image-to-video
// We can support multiple generation modes depending on whether frame images are provided
const supportedModes = ['txt2img'] as const;
if (!supportedModes.includes(generationMode as any)) {
throw new UnsupportedGenerationModeError(t('toast.runwayIncompatibleGenerationMode'));
}
const params = selectParamsSlice(state);
const prompts = selectPresetModifiedPrompts(state);
const videoFirstFrameImage = selectVideoFirstFrameImage(state);
const videoLastFrameImage = selectVideoLastFrameImage(state);
const videoParams = getVideoParameters(state);
// Get seed from params
const { seed, shouldRandomizeSeed } = params;
const finalSeed = shouldRandomizeSeed ? undefined : seed;
// Determine if this is image-to-video or text-to-video
const hasFrameImages = videoFirstFrameImage || videoLastFrameImage;
const g = new Graph(getPrefixedId('runway_video_graph'));
const positivePrompt = g.addNode({
id: getPrefixedId('positive_prompt'),
type: 'string',
value: prompts.positive,
});
// Create the runway video generation node
const runwayVideoNode = g.addNode({
id: getPrefixedId('runway_generate_video'),
// @ts-expect-error: This node is not available in the OSS application
type: 'runway_generate_video',
duration: videoParams.duration,
aspect_ratio: videoParams.aspectRatio,
seed: finalSeed,
});
// @ts-expect-error: This node is not available in the OSS application
g.addEdge(positivePrompt, 'value', runwayVideoNode, 'prompt');
// Add first frame image if provided
if (videoFirstFrameImage) {
const firstFrameImageField = zImageField.parse(videoFirstFrameImage);
// @ts-expect-error: This connection is specific to runway node
runwayVideoNode.first_frame_image = firstFrameImageField;
}
// Add last frame image if provided
if (videoLastFrameImage) {
const lastFrameImageField = zImageField.parse(videoLastFrameImage);
// @ts-expect-error: This connection is specific to runway node
runwayVideoNode.last_frame_image = lastFrameImageField;
}
// Set up metadata
g.upsertMetadata({
positive_prompt: prompts.positive,
negative_prompt: prompts.negative || '',
video_duration: videoParams.duration,
video_aspect_ratio: videoParams.aspectRatio,
seed: finalSeed,
enhance_prompt: videoParams.enhancePrompt,
generation_type: hasFrameImages ? 'image-to-video' : 'text-to-video',
});
// Add video frame images to metadata if they exist
if (hasFrameImages) {
g.upsertMetadata({
first_frame_image: videoFirstFrameImage,
last_frame_image: videoLastFrameImage,
}, 'merge');
}
g.setMetadataReceivingNode(runwayVideoNode);
return {
g,
positivePrompt,
};
};

View File

@@ -14,7 +14,6 @@ export const Prompts = memo(() => {
const modelSupportsNegativePrompt = useAppSelector(selectModelSupportsNegativePrompt);
const modelSupportsRefImages = useAppSelector(selectModelSupportsRefImages);
const hasNegativePrompt = useAppSelector(selectHasNegativePrompt);
return (
<Flex flexDir="column" gap={2}>
<ParamPositivePrompt />

View File

@@ -1,72 +0,0 @@
import type { PayloadAction, Selector } from '@reduxjs/toolkit';
import { createSelector, createSlice } from '@reduxjs/toolkit';
import type { RootState } from 'app/store/store';
import type { SliceConfig } from 'app/store/types';
import { isPlainObject } from 'es-toolkit';
import type { ImageWithDims } from 'features/controlLayers/store/types';
import { zImageWithDims } from 'features/controlLayers/store/types';
import { assert } from 'tsafe';
import z from 'zod';
const zVideoState = z.object({
_version: z.literal(1),
videoFirstFrameImage: zImageWithDims.nullable(),
videoLastFrameImage: zImageWithDims.nullable(),
generatedVideoUrl: z.string().nullable(),
});
export type VideoState = z.infer<typeof zVideoState>;
const getInitialState = (): VideoState => ({
_version: 1,
videoFirstFrameImage: null,
videoLastFrameImage: null,
generatedVideoUrl: null,
});
const slice = createSlice({
name: 'video',
initialState: getInitialState(),
reducers: {
videoFirstFrameImageChanged: (state, action: PayloadAction<ImageWithDims | null>) => {
state.videoFirstFrameImage = action.payload;
},
videoLastFrameImageChanged: (state, action: PayloadAction<ImageWithDims | null>) => {
state.videoLastFrameImage = action.payload;
},
generatedVideoUrlChanged: (state, action: PayloadAction<string | null>) => {
state.generatedVideoUrl = action.payload;
},
},
});
export const {
videoFirstFrameImageChanged,
videoLastFrameImageChanged,
generatedVideoUrlChanged,
} = slice.actions;
export const videoSliceConfig: SliceConfig<typeof slice> = {
slice,
schema: zVideoState,
getInitialState,
persistConfig: {
migrate: (state) => {
assert(isPlainObject(state));
if (!('_version' in state)) {
state._version = 1;
}
return zVideoState.parse(state);
},
},
};
export const selectVideoSlice = (state: RootState) => state.video;
const createVideoSelector = <T>(selector: Selector<VideoState, T>) => createSelector(selectVideoSlice, selector);
export const selectVideoFirstFrameImage = createVideoSelector((video) => video.videoFirstFrameImage);
export const selectVideoLastFrameImage = createVideoSelector((video) => video.videoLastFrameImage);
export const selectGeneratedVideoUrl = createVideoSelector((video) => video.generatedVideoUrl);

View File

@@ -1,102 +0,0 @@
import type { AlertStatus } from '@invoke-ai/ui-library';
import { createAction } from '@reduxjs/toolkit';
import { logger } from 'app/logging/logger';
import type { AppStore } from 'app/store/store';
import { useAppStore } from 'app/store/storeHooks';
import { extractMessageFromAssertionError } from 'common/util/extractMessageFromAssertionError';
import { withResult, withResultAsync } from 'common/util/result';
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
import { buildRunwayVideoGraph } from 'features/nodes/util/graph/generation/buildRunwayVideoGraph';
import { selectCanvasDestination } from 'features/nodes/util/graph/graphBuilderUtils';
import type { GraphBuilderArg } from 'features/nodes/util/graph/types';
import { UnsupportedGenerationModeError } from 'features/nodes/util/graph/types';
import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import { serializeError } from 'serialize-error';
import { enqueueMutationFixedCacheKeyOptions, queueApi } from 'services/api/endpoints/queue';
import { AssertionError } from 'tsafe';
const log = logger('generation');
export const enqueueRequestedCanvas = createAction('app/enqueueRequestedCanvas');
const enqueueVideo = async (store: AppStore, prepend: boolean) => {
const { dispatch, getState } = store;
dispatch(enqueueRequestedCanvas());
const state = getState();
const destination = selectCanvasDestination(state);
const buildGraphResult = await withResultAsync(async () => {
const graphBuilderArg: GraphBuilderArg = { generationMode: 'txt2img', state, manager: null };
return await buildRunwayVideoGraph(graphBuilderArg);
});
if (buildGraphResult.isErr()) {
let title = 'Failed to build graph';
let status: AlertStatus = 'error';
let description: string | null = null;
if (buildGraphResult.error instanceof AssertionError) {
description = extractMessageFromAssertionError(buildGraphResult.error);
} else if (buildGraphResult.error instanceof UnsupportedGenerationModeError) {
title = 'Unsupported generation mode';
description = buildGraphResult.error.message;
status = 'warning';
}
const error = serializeError(buildGraphResult.error);
log.error({ error }, 'Failed to build graph');
toast({
status,
title,
description,
});
return;
}
const { g, seed, positivePrompt } = buildGraphResult.value;
const prepareBatchResult = withResult(() =>
prepareLinearUIBatch({
state,
g,
prepend,
seedNode: seed,
positivePromptNode: positivePrompt,
origin: 'canvas',
destination,
})
);
if (prepareBatchResult.isErr()) {
log.error({ error: serializeError(prepareBatchResult.error) }, 'Failed to prepare batch');
return;
}
const batchConfig = prepareBatchResult.value;
const req = dispatch(
queueApi.endpoints.enqueueBatch.initiate(batchConfig, {
...enqueueMutationFixedCacheKeyOptions,
track: false,
})
);
const enqueueResult = await req.unwrap();
return { batchConfig, enqueueResult };
};
export const useEnqueueVideo = () => {
const store = useAppStore();
const enqueue = useCallback(
(prepend: boolean) => {
return enqueueVideo(store, prepend);
},
[ store]
);
return enqueue;
};

View File

@@ -16,7 +16,6 @@ import { enqueueMutationFixedCacheKeyOptions, useEnqueueBatchMutation } from 'se
import { useEnqueueCanvas } from './useEnqueueCanvas';
import { useEnqueueGenerate } from './useEnqueueGenerate';
import { useEnqueueUpscaling } from './useEnqueueUpscaling';
import { useEnqueueVideo } from './useEnqueueVideo';
const log = logger('generation');
@@ -28,7 +27,6 @@ export const useInvoke = () => {
const enqueueCanvas = useEnqueueCanvas();
const enqueueGenerate = useEnqueueGenerate();
const enqueueUpscaling = useEnqueueUpscaling();
const enqueueVideo = useEnqueueVideo();
const saveAllImagesToGallery = useAppSelector(selectSaveAllImagesToGallery);
const [_, { isLoading }] = useEnqueueBatchMutation({
@@ -52,8 +50,6 @@ export const useInvoke = () => {
return await enqueueGenerate(prepend);
case 'upscaling':
return await enqueueUpscaling(prepend);
case 'video':
return await enqueueVideo(prepend);
default:
throw new Error(`No enqueue handler for tab: ${tabName}`);
}
@@ -63,7 +59,7 @@ export const useInvoke = () => {
log.error({ error: serializeError(result.error) }, 'Failed to enqueue batch');
}
},
[enqueueCanvas, enqueueGenerate, enqueueUpscaling, enqueueVideo, enqueueWorkflows, isReady, tabName]
[enqueueCanvas, enqueueGenerate, enqueueUpscaling, enqueueWorkflows, isReady, tabName]
);
const enqueueBack = useCallback(() => {

View File

@@ -1,71 +0,0 @@
import { Flex, FormLabel, Text } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { UploadImageIconButton } from 'common/hooks/useImageUploadButton';
import { imageDTOToImageWithDims } from 'features/controlLayers/store/util';
import type { SetUpscaleInitialImageDndTargetData } from 'features/dnd/dnd';
import { setUpscaleInitialImageDndTarget } from 'features/dnd/dnd';
import { DndDropTarget } from 'features/dnd/DndDropTarget';
import { DndImage } from 'features/dnd/DndImage';
import { DndImageIcon } from 'features/dnd/DndImageIcon';
import { selectUpscaleInitialImage, upscaleInitialImageChanged } from 'features/parameters/store/upscaleSlice';
import { selectVideoFirstFrameImage, videoFirstFrameImageChanged } from 'features/parameters/store/videoSlice';
import { t } from 'i18next';
import { useCallback, useMemo } from 'react';
import { PiArrowCounterClockwiseBold } from 'react-icons/pi';
import { useImageDTO } from 'services/api/endpoints/images';
import type { ImageDTO } from 'services/api/types';
export const VideoFirstFrameImage = () => {
const dispatch = useAppDispatch();
const videoFirstFrameImage = useAppSelector(selectVideoFirstFrameImage);
const imageDTO = useImageDTO(videoFirstFrameImage?.image_name);
const onReset = useCallback(() => {
dispatch(videoFirstFrameImageChanged(null));
}, [dispatch]);
const onUpload = useCallback(
(imageDTO: ImageDTO) => {
dispatch(videoFirstFrameImageChanged(imageDTOToImageWithDims(imageDTO)));
},
[dispatch]
);
return (
<Flex justifyContent="flex-start" flexDir="column" gap={2}>
<FormLabel>First Frame Image</FormLabel>
<Flex position="relative" w={36} h={36} alignItems="center" justifyContent="center">
{!imageDTO && <UploadImageIconButton w="full" h="full" isError={!imageDTO} onUpload={onUpload} fontSize={36} />}
{imageDTO && (
<>
<DndImage imageDTO={imageDTO} borderRadius="base" />
<Flex position="absolute" flexDir="column" top={1} insetInlineEnd={1} gap={1}>
<DndImageIcon
onClick={onReset}
icon={<PiArrowCounterClockwiseBold size={16} />}
tooltip={t('common.reset')}
/>
</Flex>
<Text
position="absolute"
background="base.900"
color="base.50"
fontSize="sm"
fontWeight="semibold"
bottom={0}
left={0}
opacity={0.7}
px={2}
lineHeight={1.25}
borderTopEndRadius="base"
borderBottomStartRadius="base"
pointerEvents="none"
>{`${imageDTO.width}x${imageDTO.height}`}</Text>
</>
)}
</Flex>
</Flex>
);
};

View File

@@ -1,70 +0,0 @@
import { Flex, FormLabel, Text } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { UploadImageIconButton } from 'common/hooks/useImageUploadButton';
import { imageDTOToImageWithDims } from 'features/controlLayers/store/util';
import type { SetUpscaleInitialImageDndTargetData } from 'features/dnd/dnd';
import { setUpscaleInitialImageDndTarget } from 'features/dnd/dnd';
import { DndDropTarget } from 'features/dnd/DndDropTarget';
import { DndImage } from 'features/dnd/DndImage';
import { DndImageIcon } from 'features/dnd/DndImageIcon';
import { selectVideoLastFrameImage, videoLastFrameImageChanged } from 'features/parameters/store/videoSlice';
import { t } from 'i18next';
import { useCallback, useMemo } from 'react';
import { PiArrowCounterClockwiseBold } from 'react-icons/pi';
import { useImageDTO } from 'services/api/endpoints/images';
import type { ImageDTO } from 'services/api/types';
export const VideoLastFrameImage = () => {
const dispatch = useAppDispatch();
const videoLastFrameImage = useAppSelector(selectVideoLastFrameImage);
const imageDTO = useImageDTO(videoLastFrameImage?.image_name);
const onReset = useCallback(() => {
dispatch(videoLastFrameImageChanged(null));
}, [dispatch]);
const onUpload = useCallback(
(imageDTO: ImageDTO) => {
dispatch(videoLastFrameImageChanged(imageDTOToImageWithDims(imageDTO)));
},
[dispatch]
);
return (
<Flex justifyContent="flex-start" flexDir="column" gap={2}>
<FormLabel>Last Frame Image</FormLabel>
<Flex position="relative" w={36} h={36} alignItems="center" justifyContent="center">
{!imageDTO && <UploadImageIconButton w="full" h="full" isError={!imageDTO} onUpload={onUpload} fontSize={36} />}
{imageDTO && (
<>
<DndImage imageDTO={imageDTO} borderRadius="base" />
<Flex position="absolute" flexDir="column" top={1} insetInlineEnd={1} gap={1}>
<DndImageIcon
onClick={onReset}
icon={<PiArrowCounterClockwiseBold size={16} />}
tooltip={t('common.reset')}
/>
</Flex>
<Text
position="absolute"
background="base.900"
color="base.50"
fontSize="sm"
fontWeight="semibold"
bottom={0}
left={0}
opacity={0.7}
px={2}
lineHeight={1.25}
borderTopEndRadius="base"
borderBottomStartRadius="base"
pointerEvents="none"
>{`${imageDTO.width}x${imageDTO.height}`}</Text>
</>
)}
</Flex>
</Flex>
);
};

View File

@@ -1,41 +0,0 @@
import { Flex, StandaloneAccordion } from '@invoke-ai/ui-library';
import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { VideoFirstFrameImage } from './VideoFirstFrameImage';
import { VideoLastFrameImage } from './VideoLastFrameImage';
export const VideoSettingsAccordion = memo(() => {
const { t } = useTranslation();
const { isOpen: isOpenAccordion, onToggle: onToggleAccordion } = useStandaloneAccordionToggle({
id: 'video-settings',
defaultIsOpen: true,
});
return (
<StandaloneAccordion
label={t('upscaling.upscale')}
badges={[]}
isOpen={isOpenAccordion}
onToggle={onToggleAccordion}
>
<Flex p={4} w="full" h="full" flexDir="column" data-testid="upscale-settings-accordion">
<Flex gap={4}>
<VideoFirstFrameImage />
<VideoLastFrameImage />
</Flex>
</Flex>
</StandaloneAccordion>
);
});
VideoSettingsAccordion.displayName = 'VideoSettingsAccordion';

View File

@@ -87,6 +87,3 @@ export const selectWithModelsTab = createSelector(selectDidLoad, selectDisabledT
export const selectWithQueueTab = createSelector(selectDidLoad, selectDisabledTabs, (didLoad, disabledTabs) =>
didLoad ? !disabledTabs.includes('queue') : false
);
export const selectWithVideoTab = createSelector(selectDidLoad, selectDisabledTabs, (didLoad, disabledTabs) =>
didLoad ? !disabledTabs.includes('video') : false
);

View File

@@ -11,7 +11,6 @@ import {
selectWithModelsTab,
selectWithQueueTab,
selectWithUpscalingTab,
selectWithVideoTab,
selectWithWorkflowsTab,
} from 'features/system/store/configSlice';
import { VerticalNavBar } from 'features/ui/components/VerticalNavBar';
@@ -21,7 +20,6 @@ import { ModelsTabAutoLayout } from 'features/ui/layouts/models-tab-auto-layout'
import { navigationApi } from 'features/ui/layouts/navigation-api';
import { QueueTabAutoLayout } from 'features/ui/layouts/queue-tab-auto-layout';
import { UpscalingTabAutoLayout } from 'features/ui/layouts/upscaling-tab-auto-layout';
import { VideoTabAutoLayout } from 'features/ui/layouts/video-tab-auto-layout';
import { WorkflowsTabAutoLayout } from 'features/ui/layouts/workflows-tab-auto-layout';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo } from 'react';
@@ -44,7 +42,6 @@ const TabContent = memo(() => {
const withWorkflowsTab = useAppSelector(selectWithWorkflowsTab);
const withModelsTab = useAppSelector(selectWithModelsTab);
const withQueueTab = useAppSelector(selectWithQueueTab);
const withVideoTab = useAppSelector(selectWithVideoTab);
return (
<Flex position="relative" w="full" h="full" overflow="hidden">
@@ -54,7 +51,6 @@ const TabContent = memo(() => {
{withWorkflowsTab && tab === 'workflows' && <WorkflowsTabAutoLayout />}
{withModelsTab && tab === 'models' && <ModelsTabAutoLayout />}
{withQueueTab && tab === 'queue' && <QueueTabAutoLayout />}
{withVideoTab && tab === 'video' && <VideoTabAutoLayout />}
<SwitchingTabsLoader />
</Flex>
);

View File

@@ -1,46 +0,0 @@
import { Box, Flex } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { Prompts } from 'features/parameters/components/Prompts/Prompts';
import { VideoSettingsAccordion } from 'features/settingsAccordions/components/VideoSettingsAccordion/VideoSettingsAccordion';
import { StylePresetMenu } from 'features/stylePresets/components/StylePresetMenu';
import { StylePresetMenuTrigger } from 'features/stylePresets/components/StylePresetMenuTrigger';
import { $isStylePresetsMenuOpen } from 'features/stylePresets/store/stylePresetSlice';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { CSSProperties } from 'react';
import { memo } from 'react';
const overlayScrollbarsStyles: CSSProperties = {
height: '100%',
width: '100%',
};
export const ParametersPanelVideo = memo(() => {
const isStylePresetsMenuOpen = useStore($isStylePresetsMenuOpen);
return (
<Flex w="full" h="full" flexDir="column" gap={2}>
<StylePresetMenuTrigger />
<Flex w="full" h="full" position="relative">
<Box position="absolute" top={0} left={0} right={0} bottom={0}>
{isStylePresetsMenuOpen && (
<OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayScrollbarsParams.options}>
<Flex gap={2} flexDirection="column" h="full" w="full">
<StylePresetMenu />
</Flex>
</OverlayScrollbarsComponent>
)}
<OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayScrollbarsParams.options}>
<Flex gap={2} flexDirection="column" h="full" w="full">
<Prompts />
<VideoSettingsAccordion />
</Flex>
</OverlayScrollbarsComponent>
</Box>
</Flex>
</Flex>
);
});
ParametersPanelVideo.displayName = 'ParametersPanelVideo';

View File

@@ -12,7 +12,6 @@ import {
selectWithModelsTab,
selectWithQueueTab,
selectWithUpscalingTab,
selectWithVideoTab,
selectWithWorkflowsTab,
} from 'features/system/store/configSlice';
import { memo } from 'react';
@@ -24,7 +23,6 @@ import {
PiFrameCornersBold,
PiQueueBold,
PiTextAaBold,
PiVideoBold,
} from 'react-icons/pi';
import { Notifications } from './Notifications';
@@ -39,7 +37,6 @@ export const VerticalNavBar = memo(() => {
const withWorkflowsTab = useAppSelector(selectWithWorkflowsTab);
const withModelsTab = useAppSelector(selectWithModelsTab);
const withQueueTab = useAppSelector(selectWithQueueTab);
const withVideoTab = useAppSelector(selectWithVideoTab);
return (
<Flex flexDir="column" alignItems="center" py={6} ps={4} pe={2} gap={4} minW={0} flexShrink={0}>
@@ -51,7 +48,6 @@ export const VerticalNavBar = memo(() => {
{withWorkflowsTab && <TabButton tab="workflows" icon={<PiFlowArrowBold />} label={t('ui.tabs.workflows')} />}
{withModelsTab && <TabButton tab="models" icon={<PiCubeBold />} label={t('ui.tabs.models')} />}
{withQueueTab && <TabButton tab="queue" icon={<PiQueueBold />} label={t('ui.tabs.queue')} />}
{withVideoTab && <TabButton tab="video" icon={<PiVideoBold />} label={t('ui.tabs.video')} />}
</Flex>
<Spacer />
<StatusIndicator />

View File

@@ -15,7 +15,6 @@ import {
PiFrameCornersBold,
PiQueueBold,
PiTextAaBold,
PiVideoBold,
} from 'react-icons/pi';
import type { DockviewPanelParameters } from './auto-layout-context';
@@ -27,7 +26,6 @@ const TAB_ICONS: Record<TabName, IconType> = {
workflows: PiFlowArrowBold,
models: PiCubeBold,
queue: PiQueueBold,
video: PiVideoBold,
};
export const DockviewTabLaunchpad = memo((props: IDockviewPanelHeaderProps<DockviewPanelParameters>) => {

View File

@@ -1,35 +0,0 @@
import { Box, Flex, Text } from '@invoke-ai/ui-library';
import { useFocusRegion } from 'common/hooks/focus';
import { memo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import ReactPlayer from 'react-player';
import { useAppSelector } from 'app/store/storeHooks';
import { selectGeneratedVideoUrl } from 'features/parameters/store/videoSlice';
export const VideoPlayerPanel = memo(() => {
const { t } = useTranslation();
const ref = useRef<HTMLDivElement>(null);
const generatedVideoUrl = useAppSelector(selectGeneratedVideoUrl);
useFocusRegion('video', ref);
return (
<Flex ref={ref} w="full" h="full" flexDirection="column" gap={4}>
<Box flex={1} position="relative">
{generatedVideoUrl && <ReactPlayer
src={generatedVideoUrl}
width="75%"
height="75%"
controls={true}
style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', maxWidth: '900px'}}
/>}
{!generatedVideoUrl && <Text>No video generated</Text>}
</Box>
</Flex>
);
});
VideoPlayerPanel.displayName = 'VideoPlayerPanel';

View File

@@ -1,16 +0,0 @@
import { Box, Flex } from '@invoke-ai/ui-library';
import QueueControls from 'features/queue/components/QueueControls';
import { memo } from 'react';
import { ParametersPanelVideo } from '../components/ParametersPanels/ParametersPanelVideo';
export const VideoTabLeftPanel = memo(() => {
return (
<Flex flexDir="column" w="full" h="full" gap={2}>
<QueueControls />
<Box position="relative" w="full" h="full">
<ParametersPanelVideo />
</Box>
</Flex>
);
});
VideoTabLeftPanel.displayName = 'VideoTabLeftPanel';

View File

@@ -238,8 +238,6 @@ export class NavigationApi {
this._app.storage.set(key, api.toJSON());
}
console.log('api.panels', api.panels);
for (const panel of api.panels) {
this._registerPanel(tab, panel.id, panel);
}
@@ -447,7 +445,6 @@ export class NavigationApi {
*/
getPanel = (tab: TabName, panelId: string): PanelType | undefined => {
const key = this._getPanelKey(tab, panelId);
console.log('key', key);
return this.panels.get(key);
};
@@ -489,7 +486,6 @@ export class NavigationApi {
*/
toggleLeftPanel = (): boolean => {
const activeTab = this._app?.activeTab.get() ?? null;
console.log('activeTab', activeTab);
if (!activeTab) {
log.warn('No active tab found to toggle left panel');
return false;

View File

@@ -1,276 +0,0 @@
import type { DockviewApi, GridviewApi, IDockviewReactProps, IGridviewReactProps } from 'dockview';
import { DockviewReact, GridviewReact, LayoutPriority, Orientation } from 'dockview';
import { BoardsPanel } from 'features/gallery/components/BoardsListPanelContent';
import { GalleryPanel } from 'features/gallery/components/Gallery';
import { ImageViewerPanel } from 'features/gallery/components/ImageViewer/ImageViewerPanel';
import { FloatingLeftPanelButtons } from 'features/ui/components/FloatingLeftPanelButtons';
import { FloatingRightPanelButtons } from 'features/ui/components/FloatingRightPanelButtons';
import type {
AutoLayoutDockviewComponents,
AutoLayoutGridviewComponents,
PanelParameters,
RootLayoutGridviewComponents,
} from 'features/ui/layouts/auto-layout-context';
import { AutoLayoutProvider, useAutoLayoutContext, withPanelContainer } from 'features/ui/layouts/auto-layout-context';
import type { TabName } from 'features/ui/store/uiTypes';
import { dockviewTheme } from 'features/ui/styles/theme';
import { t } from 'i18next';
import { memo, useCallback, useEffect } from 'react';
import { DockviewTab } from './DockviewTab';
import { DockviewTabLaunchpad } from './DockviewTabLaunchpad';
import { DockviewTabProgress } from './DockviewTabProgress';
import { navigationApi } from './navigation-api';
import { PanelHotkeysLogical } from './PanelHotkeysLogical';
import {
BOARD_PANEL_DEFAULT_HEIGHT_PX,
BOARD_PANEL_MIN_HEIGHT_PX,
BOARDS_PANEL_ID,
DOCKVIEW_TAB_ID,
DOCKVIEW_TAB_LAUNCHPAD_ID,
DOCKVIEW_TAB_PROGRESS_ID,
GALLERY_PANEL_DEFAULT_HEIGHT_PX,
GALLERY_PANEL_ID,
GALLERY_PANEL_MIN_HEIGHT_PX,
LAUNCHPAD_PANEL_ID,
LEFT_PANEL_ID,
LEFT_PANEL_MIN_SIZE_PX,
MAIN_PANEL_ID,
RIGHT_PANEL_ID,
RIGHT_PANEL_MIN_SIZE_PX,
SETTINGS_PANEL_ID,
VIEWER_PANEL_ID,
} from './shared';
import { VideoTabLeftPanel } from './VideoTabLeftPanel';
import { GenerateLaunchpadPanel } from './GenerateLaunchpadPanel';
import { VideoPlayerPanel } from './VideoPlayerPanel';
const tabComponents = {
[DOCKVIEW_TAB_ID]: DockviewTab,
[DOCKVIEW_TAB_PROGRESS_ID]: DockviewTabProgress,
[DOCKVIEW_TAB_LAUNCHPAD_ID]: DockviewTabLaunchpad,
};
const mainPanelComponents: AutoLayoutDockviewComponents = {
[LAUNCHPAD_PANEL_ID]: withPanelContainer(GenerateLaunchpadPanel),
[VIEWER_PANEL_ID]: withPanelContainer(VideoPlayerPanel),
};
const initializeMainPanelLayout = (tab: TabName, api: DockviewApi) => {
navigationApi.registerContainer(tab, 'main', api, () => {
const launchpad = api.addPanel<PanelParameters>({
id: LAUNCHPAD_PANEL_ID,
component: LAUNCHPAD_PANEL_ID,
title: t('ui.panels.launchpad'),
tabComponent: DOCKVIEW_TAB_LAUNCHPAD_ID,
params: {
tab,
focusRegion: 'launchpad',
},
});
api.addPanel<PanelParameters>({
id: VIEWER_PANEL_ID,
component: VIEWER_PANEL_ID,
title: t('ui.panels.imageViewer'),
tabComponent: DOCKVIEW_TAB_PROGRESS_ID,
params: {
tab,
focusRegion: 'viewer',
},
position: {
direction: 'within',
referencePanel: launchpad.id,
},
});
launchpad.api.setActive();
});
};
const MainPanel = memo(() => {
const { tab } = useAutoLayoutContext();
const onReady = useCallback<IDockviewReactProps['onReady']>(
({ api }) => {
initializeMainPanelLayout(tab, api);
},
[tab]
);
return (
<>
<DockviewReact
disableDnd={true}
locked={true}
disableFloatingGroups={true}
dndEdges={false}
tabComponents={tabComponents}
components={mainPanelComponents}
onReady={onReady}
theme={dockviewTheme}
/>
<FloatingLeftPanelButtons />
<FloatingRightPanelButtons />
<PanelHotkeysLogical />
</>
);
});
MainPanel.displayName = 'MainPanel';
const rightPanelComponents: AutoLayoutGridviewComponents = {
[BOARDS_PANEL_ID]: withPanelContainer(BoardsPanel),
[GALLERY_PANEL_ID]: withPanelContainer(GalleryPanel),
};
const initializeRightPanelLayout = (tab: TabName, api: GridviewApi) => {
navigationApi.registerContainer(tab, 'right', api, () => {
const gallery = api.addPanel<PanelParameters>({
id: GALLERY_PANEL_ID,
component: GALLERY_PANEL_ID,
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
minimumHeight: GALLERY_PANEL_MIN_HEIGHT_PX,
params: {
tab,
focusRegion: 'gallery',
},
});
const boards = api.addPanel<PanelParameters>({
id: BOARDS_PANEL_ID,
component: BOARDS_PANEL_ID,
minimumHeight: BOARD_PANEL_MIN_HEIGHT_PX,
params: {
tab,
focusRegion: 'boards',
},
position: {
direction: 'above',
referencePanel: gallery.id,
},
});
gallery.api.setSize({ height: GALLERY_PANEL_DEFAULT_HEIGHT_PX });
boards.api.setSize({ height: BOARD_PANEL_DEFAULT_HEIGHT_PX });
});
};
const RightPanel = memo(() => {
const { tab } = useAutoLayoutContext();
const onReady = useCallback<IGridviewReactProps['onReady']>(
({ api }) => {
initializeRightPanelLayout(tab, api);
},
[tab]
);
return (
<GridviewReact
className="dockview-theme-invoke"
orientation={Orientation.VERTICAL}
components={rightPanelComponents}
onReady={onReady}
/>
);
});
RightPanel.displayName = 'RightPanel';
const leftPanelComponents: AutoLayoutGridviewComponents = {
[SETTINGS_PANEL_ID]: withPanelContainer(VideoTabLeftPanel),
};
const initializeLeftPanelLayout = (tab: TabName, api: GridviewApi) => {
navigationApi.registerContainer(tab, 'left', api, () => {
api.addPanel<PanelParameters>({
id: SETTINGS_PANEL_ID,
component: SETTINGS_PANEL_ID,
params: {
tab,
focusRegion: 'settings',
},
});
});
};
const LeftPanel = memo(() => {
const { tab } = useAutoLayoutContext();
const onReady = useCallback<IGridviewReactProps['onReady']>(
({ api }) => {
initializeLeftPanelLayout(tab, api);
},
[tab]
);
return (
<GridviewReact
className="dockview-theme-invoke"
orientation={Orientation.VERTICAL}
components={leftPanelComponents}
onReady={onReady}
/>
);
});
LeftPanel.displayName = 'LeftPanel';
const rootPanelComponents: RootLayoutGridviewComponents = {
[LEFT_PANEL_ID]: LeftPanel,
[MAIN_PANEL_ID]: MainPanel,
[RIGHT_PANEL_ID]: RightPanel,
};
const initializeRootPanelLayout = (tab: TabName, api: GridviewApi) => {
navigationApi.registerContainer(tab, 'root', api, () => {
const main = api.addPanel({
id: MAIN_PANEL_ID,
component: MAIN_PANEL_ID,
priority: LayoutPriority.High,
});
const left = api.addPanel({
id: LEFT_PANEL_ID,
component: LEFT_PANEL_ID,
minimumWidth: LEFT_PANEL_MIN_SIZE_PX,
position: {
direction: 'left',
referencePanel: main.id,
},
});
const right = api.addPanel({
id: RIGHT_PANEL_ID,
component: RIGHT_PANEL_ID,
minimumWidth: RIGHT_PANEL_MIN_SIZE_PX,
position: {
direction: 'right',
referencePanel: main.id,
},
});
left.api.setSize({ width: LEFT_PANEL_MIN_SIZE_PX });
right.api.setSize({ width: RIGHT_PANEL_MIN_SIZE_PX });
});
};
export const VideoTabAutoLayout = memo(() => {
const onReady = useCallback<IGridviewReactProps['onReady']>(({ api }) => {
initializeRootPanelLayout('video', api);
}, []);
useEffect(
() => () => {
navigationApi.unregisterTab('video');
},
[]
);
return (
<AutoLayoutProvider tab="video">
<GridviewReact
className="dockview-theme-invoke"
components={rootPanelComponents}
onReady={onReady}
orientation={Orientation.VERTICAL}
/>
</AutoLayoutProvider>
);
});
VideoTabAutoLayout.displayName = 'VideoTabAutoLayout';

View File

@@ -1,7 +1,7 @@
import { isPlainObject } from 'es-toolkit';
import { z } from 'zod';
export const zTabName = z.enum(['generate', 'canvas', 'upscaling', 'workflows', 'models', 'queue', 'video']);
export const zTabName = z.enum(['generate', 'canvas', 'upscaling', 'workflows', 'models', 'queue']);
export type TabName = z.infer<typeof zTabName>;
const zPartialDimensions = z.object({

View File

@@ -12,7 +12,6 @@ import { boardIdSelected, galleryViewChanged, imageSelected } from 'features/gal
import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useNodeExecutionState';
import { isImageField, isImageFieldCollection } from 'features/nodes/types/common';
import { zNodeStatus } from 'features/nodes/types/invocation';
import { generatedVideoUrlChanged } from 'features/parameters/store/videoSlice';
import type { LRUCache } from 'lru-cache';
import { boardsApi } from 'services/api/endpoints/boards';
import { getImageDTOSafe, imagesApi } from 'services/api/endpoints/images';
@@ -205,15 +204,6 @@ export const buildOnInvocationComplete = (
return imageDTOs;
};
const getResultVideoDTOs = async (data: S['InvocationCompleteEvent']): Promise<string | null> => {
// @ts-expect-error: This is a workaround to get the video name from the result
if (data.invocation.type === 'runway_generate_video') {
// @ts-expect-error: This is a workaround to get the video name from the result
return data.result.video.video_name;
}
return null;
};
return async (data: S['InvocationCompleteEvent']) => {
if (finishedQueueItemIds.has(data.item_id)) {
log.trace({ data } as JsonObject, `Received event for already-finished queue item ${data.item_id}`);
@@ -235,11 +225,6 @@ export const buildOnInvocationComplete = (
await addImagesToGallery(data);
const videoUrl = await getResultVideoDTOs(data);
if (videoUrl) {
dispatch(generatedVideoUrlChanged(videoUrl));
}
$lastProgressEvent.set(null);
};
};