Compare commits

...

346 Commits

Author SHA1 Message Date
psychedelicious
d16eef4e66 chore: bump version to v6.6.0 2025-09-08 14:01:02 +10:00
psychedelicious
681ff2b2b3 chore(ui): update whatsnew 2025-09-08 14:01:02 +10:00
psychedelicious
0d81b4ce98 tidy(ui): make names a bit clearer 2025-09-08 13:54:23 +10:00
psychedelicious
99f1667ced tidy(ui): remove unused dependency 2025-09-08 13:54:23 +10:00
psychedelicious
aa5597ab4d feat(ui): use resize observer directly in component 2025-09-08 13:54:23 +10:00
psychedelicious
9bbb8e8a5e feat(ui): simpler strategy to conditionally render slider brush width 2025-09-08 13:54:23 +10:00
psychedelicious
f284d282c1 feat(ui): color picker number input outline styling 2025-09-08 13:54:23 +10:00
Attila Cseh
4231488da6 number input height set 2025-09-08 13:54:23 +10:00
Attila Cseh
a014867e68 slider number input height set 2025-09-08 13:54:23 +10:00
Attila Cseh
22654fbc9c redundant translations removed 2025-09-08 13:54:23 +10:00
Attila Cseh
daa4fd751c ToolWidthPicker refactored 2025-09-08 13:54:23 +10:00
Attila Cseh
3fd265c333 slider for brush and eraser tool 2025-09-08 13:54:23 +10:00
psychedelicious
26a3a9130c Revert "build(ui): port clean translations script to js"
This reverts commit 8a00d855b4.
2025-09-08 11:20:55 +10:00
psychedelicious
3dfeaab4b2 Revert "build(ui): add package script to check and clean translatoins"
This reverts commit 9610f34dd4.
2025-09-08 11:20:55 +10:00
psychedelicious
a33707cc76 Revert "ci: add translation string check to frontend checks"
This reverts commit 98945a4560.
2025-09-08 11:20:55 +10:00
psychedelicious
21e13daf6e Revert "chore(ui): clean translations"
This reverts commit a0dceecab9.
2025-09-08 11:20:55 +10:00
psychedelicious
fa2614ee02 Revert "tidy(ui): remove python clean translations script"
This reverts commit 8a81c05caf.
2025-09-08 11:20:55 +10:00
Hosted Weblate
4be6ddb23d translationBot(ui): update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/
Translation: InvokeAI/Web UI
2025-09-05 12:28:33 +10:00
Riccardo Giovanetti
bba0e01926 translationBot(ui): update translation (Italian)
Currently translated at 98.6% (2093 of 2122 strings)

Co-authored-by: Riccardo Giovanetti <riccardo.giovanetti@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/it/
Translation: InvokeAI/Web UI
2025-09-05 12:28:33 +10:00
psychedelicious
20d57d5ccf gh: update pr template 2025-09-05 11:27:02 +10:00
psychedelicious
d9121271a2 fix(ui): rehydration + redux migration design issue
Certain items in redux are ephemeral and omitted from persisted slices.
On rehydration, we need to inject these values back into the slice.

But there was an issue taht could prevent slice migrations from running
during rehydration.

The migrations look for the `_version` key in state and migrate the
slice accordingly.

The logic that merged in the ephemeral values accidentally _also_ merged
in the `_version` key if it didn't already exist. This happened _before_
migrations are run.

This causes problems for slices that didn't have a `_version` key and
then have one added via migration.

For example, the params slice didn't have a `_version` key until the
previous commit, which added `_version` and changed some other parts of
state in a migration.

On first load of the updated code, we have a catch-22 kinda situation:
- The persisted params slice is the old version. It needs to have both
`_version` and some other data added to it.
- We deserialize the state and then merge in ephemeral values. This
inadvertnetly also merged in the `_version` key.
- We run the slice migration. It sees there is a `_version` key and
thinks it doesn't need to run. The extra data isn't added to the slice.
The slice is parsed against its zod schema and fails because the new
data is missing.
- Because the parse failed, we treat the user's persisted data as
invalid and overwrite it with initial state, potentially causing data
loss.

The fix is to be more selective when merging in the ephemeral state
before migration - this is now done by checking which keys are on the
persist denylist and only adding those key.
2025-09-05 11:27:02 +10:00
psychedelicious
30b487c71c tidy(ui): remove unused x/y coords from params slice 2025-09-05 11:27:02 +10:00
psychedelicious
8a81c05caf tidy(ui): remove python clean translations script 2025-09-05 11:02:37 +10:00
psychedelicious
a0dceecab9 chore(ui): clean translations 2025-09-05 11:02:37 +10:00
psychedelicious
98945a4560 ci: add translation string check to frontend checks 2025-09-05 11:02:37 +10:00
psychedelicious
9610f34dd4 build(ui): add package script to check and clean translatoins 2025-09-05 11:02:37 +10:00
psychedelicious
8a00d855b4 build(ui): port clean translations script to js 2025-09-05 11:02:37 +10:00
psychedelicious
25430f04c5 chore: bump version to v6.6.0rc2 2025-09-04 16:43:41 +10:00
psychedelicious
b2b53c4481 fix(ui): set a react key on the current image viewer's components
This tells react that the component is a new instance each time we
change the image. Which, in turn, prevents a flash of the
previously-selected image during image switching and
progress-image-to-output-image-ing.
2025-09-04 16:35:40 +10:00
psychedelicious
c6696d7913 fix(ui): ensure origin is set correctly for generate tab batches
This prevents an issue in the image viewer's logic for simulating the
progress image "resolving" to a completed image
2025-09-04 16:35:40 +10:00
psychedelicious
8bcb6648f1 fix(ui): stop dragging when user clicks mmb once
This has been an issue for a long time. I suspect it wasn't noticed
until now because it's finicky to trigger - you have to click and
release very quickly, without moving the mouse at all.
2025-09-04 16:16:04 +10:00
psychedelicious
0ee360ba6c fix(ui): show fallback when no image is selected 2025-09-04 16:13:01 +10:00
psychedelicious
09bbe3eef9 fix(ui): clear gallery selection when switching boards and there are no items in the new board 2025-09-04 16:13:01 +10:00
psychedelicious
d14b7a48f5 fix(ui): clear gallery selection when last image on selected board is deleted 2025-09-04 16:13:01 +10:00
Mary Hipp
1db55b0ffa cleanup 2025-09-03 10:11:32 -04:00
Mary Hipp
3104a1baa6 remove crossOrigin for thumbnail loading 2025-09-03 10:11:32 -04:00
psychedelicious
0e523ca2c1 fix(ui): browser image caching cors race condition
Must set cross origin whenever we load an image from a URL to prevent
race conditions where browser caches an image with no CORS, then canvas
attempts to load it with CORS, resulting in browser rejecting the
request before it is made
2025-09-03 10:11:32 -04:00
psychedelicious
75daef2aba fix(ui): fix situation where progress images are super tiny
Missed a spot
2025-09-03 22:56:55 +10:00
psychedelicious
b036b18986 chore: bump version to v6.6.0rc1 2025-09-03 18:02:37 +10:00
Hosted Weblate
93535fa3c2 translationBot(ui): update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/
Translation: InvokeAI/Web UI
2025-09-03 17:57:27 +10:00
Riccardo Giovanetti
dcafb44f8a translationBot(ui): update translation (Italian)
Currently translated at 98.6% (2088 of 2117 strings)

Co-authored-by: Riccardo Giovanetti <riccardo.giovanetti@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/it/
Translation: InvokeAI/Web UI
2025-09-03 17:57:27 +10:00
Mary Hipp
44b1d8d1fc remove video base models from image aspect/ratio logic 2025-09-03 10:22:14 +10:00
Attila Cseh
6f70a6bd10 prettier fix 2025-09-02 19:23:24 +10:00
Attila Cseh
0546aeed1d code review changes 2025-09-02 19:23:24 +10:00
Attila Cseh
8933f3f5dd LoRA weight default values turned into constant 2025-09-02 19:23:24 +10:00
Attila Cseh
29cdefe873 type conversion fixed 2025-09-02 19:23:24 +10:00
Attila Cseh
df299bb37f python source code reformatted 2025-09-02 19:23:24 +10:00
Attila Cseh
481fb42371 lint errors fixed 2025-09-02 19:23:24 +10:00
Attila Cseh
631a04b48c LoRA default weight 2025-09-02 19:23:24 +10:00
Attila Cseh
547e1941f4 code review changes 2025-09-02 19:16:26 +10:00
Attila Cseh
031d25ed63 switchable foreground/background colors 2025-09-02 19:16:26 +10:00
Riccardo Giovanetti
27f4af0eb4 translationBot(ui): update translation (Italian)
Currently translated at 98.6% (2087 of 2116 strings)

Co-authored-by: Riccardo Giovanetti <riccardo.giovanetti@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/it/
Translation: InvokeAI/Web UI
2025-09-02 15:05:51 +10:00
psychedelicious
e0a0617093 chore(ui): bump dockview
This brings in a fix for Chrome that allowed you to drag tabs and split
the panels.

Closes #8449
2025-09-02 11:05:41 +10:00
psychedelicious
e6a763b887 fix(ui): move getItemsPerRow to frontend src dir
Not sure how but it was in repo root

Closes #8509
2025-09-02 11:02:56 +10:00
psychedelicious
3c9c49f7d9 feat(ui): add readiness checks for LoRAs
If incompatible LoRAs are added, prevent Invoking.

The logic to prevent adding incompatible LoRAs to graphs already
existed. This does not fix any generation bugs; just a visual
inconsistency where it looks like Invoke would use an incompatible LoRA.
2025-09-01 14:41:03 +10:00
Attila Cseh
26690d47b7 lint errors fixed 2025-09-01 14:34:35 +10:00
Attila Cseh
fcaff6ce09 remove LoRAs for recall use all 2025-09-01 14:34:35 +10:00
Damian
afd7296cb2 Add 'sd-2' to supported negative prompt base models
add back negative prompt support for sd2
2025-08-31 10:20:31 -04:00
psychedelicious
d6f42c76d5 fix(app): board count queries not getting categories as params 2025-08-29 11:07:52 +10:00
Mary Hipp
68f39fe907 cleanup 2025-08-28 16:38:48 -04:00
Mary Hipp
23a528545f match screen capture button to the others 2025-08-28 16:38:48 -04:00
Mary Hipp
c69d04a7f0 handle large videos 2025-08-28 15:29:47 -04:00
Mary Hipp
60f1e2d7ad do not show negative prompt for video 2025-08-28 12:59:23 -04:00
Mary Hipp
cb386bec28 do not show reference images on video tab 2025-08-28 12:59:23 -04:00
Mary Hipp
f29ceb3f12 add translations 2025-08-28 10:17:00 -04:00
Mary Hipp
4f51bc9421 add credit estimate for video generation 2025-08-28 10:17:00 -04:00
Mary Hipp
0c41abab79 add label for starting image field 2025-08-28 10:17:00 -04:00
Mary Hipp
cb457c3402 default resolution to 1080p 2025-08-28 10:17:00 -04:00
Mary Hipp
606ad73814 use first video model if none selected 2025-08-28 10:17:00 -04:00
psychedelicious
fe70bd538a fix(ui): hide unused queue actions menu item category 2025-08-28 10:17:00 -04:00
psychedelicious
b5c7316c0a chore(ui): lint 2025-08-28 10:17:00 -04:00
psychedelicious
460aec03ea fix(ui): more video translations 2025-08-28 10:17:00 -04:00
psychedelicious
6730d86a13 fix(ui): make ctx menu star label not refer to iamges 2025-08-28 10:17:00 -04:00
psychedelicious
c4bc03cb1f fix(ui): make ctx menu download tooltip not refer to iamges 2025-08-28 10:17:00 -04:00
psychedelicious
136ee28199 feat(ui): remove unimplemented context menu items for video 2025-08-28 10:17:00 -04:00
psychedelicious
2c6d266c0a fix(ui): metadata viewer translations 2025-08-28 10:17:00 -04:00
psychedelicious
f779920eaa chore(ui): lint 2025-08-28 10:17:00 -04:00
psychedelicious
01bef5d165 fix(ui): do not highlight starting frame image in red when it is not required 2025-08-28 10:17:00 -04:00
psychedelicious
72851d3e84 feat(ui): tweak video settings padding 2025-08-28 10:17:00 -04:00
psychedelicious
4ba85c62ca feat(ui): add border around starting frame image 2025-08-28 10:17:00 -04:00
psychedelicious
313aedb00a fix(ui): graph builder check for veo 2025-08-28 10:17:00 -04:00
psychedelicious
85bd324d74 tweak(ui): nav bar divider not so bright 2025-08-28 10:17:00 -04:00
psychedelicious
4a04411e74 fix(ui): tab hotkeys for video 2025-08-28 10:17:00 -04:00
psychedelicious
299a4db3bb chore(ui): lint 2025-08-28 10:17:00 -04:00
psychedelicious
390faa592c chore: ruff 2025-08-28 10:17:00 -04:00
Mary Hipp
2463aeb84a studio init action for video tab 2025-08-28 10:17:00 -04:00
Mary Hipp
ec8df163d1 launchpad cleanup 2025-08-28 10:17:00 -04:00
Mary Hipp
a198b7da78 fix view on large screens, restore auth for screen capture 2025-08-28 10:17:00 -04:00
Mary Hipp
fb11770852 rearrange image | video | asset for boards 2025-08-28 10:17:00 -04:00
Mary Hipp
6b6f3d56f7 add option for video upsell, rearrange navigation bar and gallery tabs 2025-08-28 10:17:00 -04:00
Mary Hipp
29d00eef9a hide video features if video is disabled 2025-08-28 10:17:00 -04:00
psychedelicious
6972cd708d feat(ui): delete confirmation for videos 2025-08-28 10:17:00 -04:00
psychedelicious
82893804ff feat(ui): metadata recall for videos 2025-08-28 10:17:00 -04:00
psychedelicious
47ffe365bc fix(ui): do not store whole model config in state 2025-08-28 10:17:00 -04:00
psychedelicious
f7b03b1e63 fix(ui): do not change canvas bbox on video model change 2025-08-28 10:17:00 -04:00
psychedelicious
356e38e82a feat(ui): use correct model config object in video graph builders 2025-08-28 10:17:00 -04:00
psychedelicious
5ea077bb8c feat(ui): add selector to get model config for current video model 2025-08-28 10:17:00 -04:00
psychedelicious
3c4b303555 feat(ui): simplify and consolidate video capture logic 2025-08-28 10:17:00 -04:00
psychedelicious
b8651cb1a2 fix(ui): rebase conflict 2025-08-28 10:17:00 -04:00
Mary Hipp
a6527c0ba1 lint again 2025-08-28 10:17:00 -04:00
Mary Hipp
6e40eca754 lint 2025-08-28 10:17:00 -04:00
Mary Hipp
53fab17c33 use context to track video ref so that toolbar can also save current frame 2025-08-28 10:17:00 -04:00
Mary Hipp
3876d88b3c add save frame functionality 2025-08-28 10:17:00 -04:00
Mary Hipp
82b4526691 add video_count and asset_count to boards UI 2025-08-28 10:17:00 -04:00
Mary Hipp
f56ba11394 add asset_count to BoardDTO and split it out from image count 2025-08-28 10:17:00 -04:00
Mary Hipp
32eb5190f2 add video_count to boardDTO 2025-08-28 10:17:00 -04:00
Mary Hipp
72e378789d video metadata support 2025-08-28 10:17:00 -04:00
Mary Hipp
f10ddb0cab split out video aspect/ratio into its own components 2025-08-28 10:17:00 -04:00
Mary Hipp
286127077d updates for new model type 2025-08-28 10:17:00 -04:00
Mary Hipp
36278bc044 add UI support for new model type Video 2025-08-28 10:17:00 -04:00
Mary Hipp
7a1c7ca43a add Video as new model type 2025-08-28 10:17:00 -04:00
psychedelicious
8303d567d5 docs(ui): add note about visual jank in gallery 2025-08-28 10:17:00 -04:00
psychedelicious
1fe19c1242 fix(ui): use correct placeholder for vidoes 2025-08-28 10:17:00 -04:00
psychedelicious
127a43865c fix(ui): locate in gallery, galleryview when selecting image/video 2025-08-28 10:17:00 -04:00
psychedelicious
24a48884cb chore(ui): lint 2025-08-28 10:17:00 -04:00
psychedelicious
47cee816fd chore(ui): dpdm 2025-08-28 10:17:00 -04:00
psychedelicious
90bacaddda feat(ui): video dnd 2025-08-28 10:17:00 -04:00
psychedelicious
c0cc9f421e fix(ui): generate tab graph builder 2025-08-28 10:17:00 -04:00
psychedelicious
dbb9032648 fix(ui): iterations works for video models 2025-08-28 10:17:00 -04:00
psychedelicious
b9e32e59a2 fix(ui): missing tranlsation 2025-08-28 10:17:00 -04:00
psychedelicious
545a1d8737 fix(ui): fetching imageDTO for video 2025-08-28 10:17:00 -04:00
psychedelicious
c4718403a2 tidy(ui): remove unused VideoAtPosition component 2025-08-28 10:17:00 -04:00
psychedelicious
eb308b1ff7 feat(ui): simpler layout for video player 2025-08-28 10:17:00 -04:00
Mary Hipp
a277bea804 fix video styling 2025-08-28 10:17:00 -04:00
Mary Hipp
30619c0420 add runway back as a model and allow runway and veo3 to live together in peace and harmony 2025-08-28 10:17:00 -04:00
Mary Hipp
504d8e32be add runway to backend 2025-08-28 10:17:00 -04:00
Mary Hipp
f21229cd14 update redux selection to have a list of images and/or videos, update image viewer to show either image or video depending on what is selected 2025-08-28 10:17:00 -04:00
Mary Hipp
640ec676c3 lint 2025-08-28 10:17:00 -04:00
Mary Hipp
6370412e9c tsc 2025-08-28 10:17:00 -04:00
Mary Hipp
edec2c2775 lint the dang thing 2025-08-28 10:17:00 -04:00
psychedelicious
bd38be31d8 gallery 2025-08-28 10:17:00 -04:00
psychedelicious
b938ae0a7e Revert "feat(ui): consolidated gallery (wip)"
This reverts commit 12b70bca67.
2025-08-28 10:17:00 -04:00
Mary Hipp
6e5b1ed55f add videos to change board modal 2025-08-28 10:17:00 -04:00
Mary Hipp
5970bd38c2 add resolution as a generation setting 2025-08-28 10:17:00 -04:00
Mary Hipp
e046417cf5 replace runway with veo, build out veo3 model support 2025-08-28 10:17:00 -04:00
Mary Hipp
27a2cd19bd add Veo3 model support to backend 2025-08-28 10:17:00 -04:00
psychedelicious
0df631b802 feat(ui): consolidated gallery (wip) 2025-08-28 10:17:00 -04:00
psychedelicious
5bb7cd168d feat(ui): gallery optimistic updates for video 2025-08-28 10:17:00 -04:00
psychedelicious
b4ba84ad35 fix(ui): panel names on video tab 2025-08-28 10:17:00 -04:00
Mary Hipp
d1628f51c9 stubbing out change board functionality 2025-08-28 10:17:00 -04:00
Mary Hipp
17c1304ce2 hook up starring, unstarring, and deleting single videos (no multiselect yet), adapt context menus to work for both images and videos and start on video context menu 2025-08-28 10:17:00 -04:00
Mary Hipp
cc9a85f7d0 add readiness logic to video tab 2025-08-28 10:17:00 -04:00
psychedelicious
7e2999649a feat(ui): more video stuff 2025-08-28 10:17:00 -04:00
psychedelicious
1473142f73 feat(ui): fiddle w/ video stuff 2025-08-28 10:17:00 -04:00
psychedelicious
49343546e7 feat(ui): fiddle w/ video stuff 2025-08-28 10:17:00 -04:00
psychedelicious
39d5879405 chore: ruff 2025-08-28 10:17:00 -04:00
psychedelicious
4b4ec29a09 feat(nodes): update VideoField & VideoOutput 2025-08-28 10:17:00 -04:00
psychedelicious
dc6811076f feat(ui): add dnd target for video start frame 2025-08-28 10:17:00 -04:00
Mary Hipp
0568784ee9 add duration and aspect ratio to video settings 2025-08-28 10:17:00 -04:00
Mary Hipp
895eac6bcd integrating video into gallery - thinking maybe a new category of image would make more senes 2025-08-28 10:17:00 -04:00
Mary Hipp
fe0efa9bdf add noop video router 2025-08-28 10:17:00 -04:00
Mary Hipp
acabc8bd54 add video models 2025-08-28 10:17:00 -04:00
Mary Hipp
89f999af08 combine nodes that generate and save videos 2025-08-28 10:17:00 -04:00
Mary Hipp
9ae76bef51 build out adhoc video saving graph 2025-08-28 10:17:00 -04:00
Mary Hipp
0999b43616 push up updates for VideoField 2025-08-28 10:17:00 -04:00
Mary Hipp
e6e4f58163 update VideoField 2025-08-28 10:17:00 -04:00
Mary Hipp
b371930e02 split out RunwayVideoOutput from VideoOutput 2025-08-28 10:17:00 -04:00
Mary Hipp
9b50e2303b rough rough POC of video tab 2025-08-28 10:17:00 -04:00
Mary Hipp
49d1810991 video_output support 2025-08-28 10:17:00 -04:00
psychedelicious
b1b009f7b8 chore: bump version to v6.5.1 2025-08-28 22:57:14 +10:00
psychedelicious
3431e6385c chore: uv lock 2025-08-28 22:57:14 +10:00
psychedelicious
5db1027d32 Pin sentencepiece version in pyproject.toml
Pin sentencepiece version to 0.2.0 to avoid coredump issues.
2025-08-28 22:57:14 +10:00
Hosted Weblate
579f182fe9 translationBot(ui): update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/
Translation: InvokeAI/Web UI
2025-08-28 22:51:40 +10:00
Riccardo Giovanetti
55bf41f63f translationBot(ui): update translation (Italian)
Currently translated at 98.6% (2053 of 2082 strings)

Co-authored-by: Riccardo Giovanetti <riccardo.giovanetti@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/it/
Translation: InvokeAI/Web UI
2025-08-28 22:51:40 +10:00
psychedelicious
fc32fd2d2e fix(ui): progress image renders at physical size 2025-08-28 22:47:52 +10:00
psychedelicious
a2b6536078 fix(ui): konva caching opt-out doesn't do what i thought it would 2025-08-28 22:45:03 +10:00
Mary Hipp Rogers
144c54a6c8 Revert "video_output support"
This reverts commit 453ef1a220.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
ca40daeb97 Revert "rough rough POC of video tab"
This reverts commit e89266bfe3.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
e600cdc826 Revert "split out RunwayVideoOutput from VideoOutput"
This reverts commit 97719b0aab.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
b7c52f33dc Revert "update VideoField"
This reverts commit bd251f8cce.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
e78157fcf0 Revert "push up updates for VideoField"
This reverts commit 94ba840948.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
7d7b98249f Revert "build out adhoc video saving graph"
This reverts commit 07565d4015.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
f5bf84f304 Revert "combine nodes that generate and save videos"
This reverts commit eff9c7b92f.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
c30d5bece2 Revert "add video models"
This reverts commit 295b5a20a8.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
27845b2f1b Revert "add noop video router"
This reverts commit e9c4e12454.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
bad6eea077 Revert "integrating video into gallery - thinking maybe a new category of image would make more senes"
This reverts commit 5c93e53195.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
9c26ac5ce3 Revert "add duration and aspect ratio to video settings"
This reverts commit 4d8bcad15b.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
b7306bb5c9 Revert "feat(ui): add dnd target for video start frame"
This reverts commit 530d20c1be.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
0c115177b2 Revert "feat(nodes): update VideoField & VideoOutput"
This reverts commit 67de3f2d9b.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
5aae41b5bb Revert "chore: ruff"
This reverts commit 9380d8901c.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
7ad09a2f79 Revert "feat(ui): fiddle w/ video stuff"
This reverts commit f98bbc32dd.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
5a6d3639b7 Revert "feat(ui): fiddle w/ video stuff"
This reverts commit 79e8482b27.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
84617d3df2 Revert "feat(ui): more video stuff"
This reverts commit 963c2ec60c.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
e05f30749e Revert "add readiness logic to video tab"
This reverts commit 288ac0a293.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
88a2e27338 Revert "hook up starring, unstarring, and deleting single videos (no multiselect yet), adapt context menus to work for both images and videos and start on video context menu"
This reverts commit a918198d4f.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
15a6fd76c8 Revert "stubbing out change board functionality"
This reverts commit 67042e6dec.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
6adb46a86c Revert "fix(ui): panel names on video tab"
This reverts commit 64dfa125d2.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
e8a74eb79d Revert "feat(ui): gallery optimistic updates for video"
This reverts commit 0ec6d33086.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
dcd716c384 Revert "feat(ui): consolidated gallery (wip)"
This reverts commit 6ef1c2a5e1.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
56697635dd Revert "add Veo3 model support to backend"
This reverts commit 49d569ec59.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
5b5657e292 Revert "replace runway with veo, build out veo3 model support"
This reverts commit d95a698ebd.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
ad3dfbe1ed Revert "add resolution as a generation setting"
This reverts commit b71829a827.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
59ddc4f7b0 Revert "add videos to change board modal"
This reverts commit 45b4432833.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
4653b79f12 Revert "Revert "feat(ui): consolidated gallery (wip)""
This reverts commit 637d19c22b.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
778d6f167f Revert "gallery"
This reverts commit aa4e3adadb.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
05c71f50f1 Revert "lint the dang thing"
This reverts commit 1b0d599dc2.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
406e0be39c Revert "tsc"
This reverts commit 7828102b67.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
0d71234a12 Revert "lint"
This reverts commit b377b80446.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
e38019bb70 Revert "update redux selection to have a list of images and/or videos, update image viewer to show either image or video depending on what is selected"
This reverts commit 8df3067599.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
a879880b42 Revert "add runway to backend"
This reverts commit f631b5178f.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
71c8accbfe Revert "add runway back as a model and allow runway and veo3 to live together in peace and harmony"
This reverts commit b2026d9c00.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
154fb99daf Revert "fix video styling"
This reverts commit 3d9889e272.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
0df476ce13 Revert "feat(ui): simpler layout for video player"
This reverts commit 3a1cedbced.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
e7ad830fa9 Revert "tidy(ui): remove unused VideoAtPosition component"
This reverts commit e55d39a20b.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
e81e0a8286 Revert "fix(ui): fetching imageDTO for video"
This reverts commit fbf8aa17c8.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
d0f7e72cbb Revert "fix(ui): missing tranlsation"
This reverts commit 89efe9c2b1.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
fdead4fb8c Revert "fix(ui): iterations works for video models"
This reverts commit 24f22d539f.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
31c9945b32 Revert "fix(ui): generate tab graph builder"
This reverts commit 84dc4e4ea9.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
22de8a4b12 Revert "feat(ui): video dnd"
This reverts commit f5fdba795a.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
89cb3c3230 Revert "chore(ui): dpdm"
This reverts commit 6a7fe6668b.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
7bb99ece4e Revert "chore(ui): lint"
This reverts commit 55139bb169.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
28f040123f Revert "fix(ui): locate in gallery, galleryview when selecting image/video"
This reverts commit 26fe937d97.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
1be3a4db64 Revert "fix(ui): use correct placeholder for vidoes"
This reverts commit 7e031e9c01.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
cb44c995d2 Revert "docs(ui): add note about visual jank in gallery"
This reverts commit 2d9c82da85.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
9b9b35c315 Revert "add Video as new model type"
This reverts commit fb0a924918.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
f6edab6032 Revert "add UI support for new model type Video"
This reverts commit c6f2d127ef.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
f79665b023 Revert "updates for new model type"
This reverts commit 23cde86bc4.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
6b1bc7a87d Revert "split out video aspect/ratio into its own components"
This reverts commit 6c375b228e.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
c6f2994c84 Revert "video metadata support"
This reverts commit b16d1a943d.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
0cff67ff23 Revert "add video_count to boardDTO"
This reverts commit 1cc6893d0d.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
e957c11c9a Revert "add asset_count to BoardDTO and split it out from image count"
This reverts commit d4378d9f2a.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
4baa685c7a Revert "add video_count and asset_count to boards UI"
This reverts commit e36490c2ec.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
1bd5907a12 Revert "add save frame functionality"
This reverts commit 6a20271dba.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
2fd56e6029 Revert "use context to track video ref so that toolbar can also save current frame"
This reverts commit 1bf25fadb3.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
b0548edc8c Revert "lint"
This reverts commit 378f33bc92.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
41d781176f Revert "lint again"
This reverts commit 41e1697e79.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
8709de0b33 Revert "fix(ui): rebase conflict"
This reverts commit bc6dd12083.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
af43fe2fd4 Revert "feat(ui): simplify and consolidate video capture logic"
This reverts commit c5a76806c1.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
ebbb11c3b1 Revert "feat(ui): add selector to get model config for current video model"
This reverts commit 5cabc37a87.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
0fc8c08da3 Revert "feat(ui): use correct model config object in video graph builders"
This reverts commit 9fcba3b876.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
bfadcffe3c Revert "fix(ui): do not change canvas bbox on video model change"
This reverts commit 8eb3f40e1b.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
49c2332c13 Revert "fix(ui): do not store whole model config in state"
This reverts commit b2ed3c99d4.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
dacef158c4 Revert "feat(ui): metadata recall for videos"
This reverts commit 4c32b2a123.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
0c34d8201e Revert "feat(ui): delete confirmation for videos"
This reverts commit 505c75a5ab.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
77132075ff Revert "hide video features if video is disabled"
This reverts commit 0de5097207.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
f008d3b0b2 Revert "add option for video upsell, rearrange navigation bar and gallery tabs"
This reverts commit 4845d31857.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
4e66ccefe8 Revert "rearrange image | video | asset for boards"
This reverts commit 8a60def51f.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
5d0ed45326 Revert "fix view on large screens, restore auth for screen capture"
This reverts commit 1f526a1c27.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
379d633ac6 Revert "launchpad cleanup"
This reverts commit ab41f71a36.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
93bba1b692 Revert "studio init action for video tab"
This reverts commit 431fd83a43.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
667e175ab7 Revert "chore: ruff"
This reverts commit 3ae99df091.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
de146aa4aa Revert "chore(ui): lint"
This reverts commit 36c16d2781.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
ed9c2c8208 Revert "fix(ui): tab hotkeys for video"
This reverts commit 20813b5615.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
9d984878f3 Revert "tweak(ui): nav bar divider not so bright"
This reverts commit 269d4fe670.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
585eb8c69d Revert "fix(ui): graph builder check for veo"
This reverts commit 239fb86a46.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
c105bae127 Revert "feat(ui): add border around starting frame image"
This reverts commit 8642e8881d.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
c39f26266f Revert "feat(ui): tweak video settings padding"
This reverts commit 842d729ec8.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
47dffd123a Revert "fix(ui): do not highlight starting frame image in red when it is not required"
This reverts commit 0b05b24e9a.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
b946ec3172 Revert "chore(ui): lint"
This reverts commit 8c2e6a3988.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
024c02329d Revert "fix(ui): metadata viewer translations"
This reverts commit 2a6cfde488.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
4b43b59472 Revert "feat(ui): remove unimplemented context menu items for video"
This reverts commit a6b0581939.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
d11f115e1a Revert "fix(ui): make ctx menu download tooltip not refer to iamges"
This reverts commit e4f24c4dc4.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
92253ce854 Revert "fix(ui): make ctx menu star label not refer to iamges"
This reverts commit ec793cb636.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
0ebbfa90c9 Revert "fix(ui): more video translations"
This reverts commit 0d827d8306.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
fdfee11e37 Revert "chore(ui): lint"
This reverts commit 3971382a6d.
2025-08-28 08:32:47 -04:00
Mary Hipp Rogers
6091bf4f60 Revert "fix(ui): hide unused queue actions menu item category"
This reverts commit 07271ca468.
2025-08-28 08:32:47 -04:00
psychedelicious
07271ca468 fix(ui): hide unused queue actions menu item category 2025-08-28 08:23:58 -04:00
psychedelicious
3971382a6d chore(ui): lint 2025-08-28 08:23:58 -04:00
psychedelicious
0d827d8306 fix(ui): more video translations 2025-08-28 08:23:58 -04:00
psychedelicious
ec793cb636 fix(ui): make ctx menu star label not refer to iamges 2025-08-28 08:23:58 -04:00
psychedelicious
e4f24c4dc4 fix(ui): make ctx menu download tooltip not refer to iamges 2025-08-28 08:23:58 -04:00
psychedelicious
a6b0581939 feat(ui): remove unimplemented context menu items for video 2025-08-28 08:23:58 -04:00
psychedelicious
2a6cfde488 fix(ui): metadata viewer translations 2025-08-28 08:23:58 -04:00
psychedelicious
8c2e6a3988 chore(ui): lint 2025-08-28 08:23:58 -04:00
psychedelicious
0b05b24e9a fix(ui): do not highlight starting frame image in red when it is not required 2025-08-28 08:23:58 -04:00
psychedelicious
842d729ec8 feat(ui): tweak video settings padding 2025-08-28 08:23:58 -04:00
psychedelicious
8642e8881d feat(ui): add border around starting frame image 2025-08-28 08:23:58 -04:00
psychedelicious
239fb86a46 fix(ui): graph builder check for veo 2025-08-28 08:23:58 -04:00
psychedelicious
269d4fe670 tweak(ui): nav bar divider not so bright 2025-08-28 08:23:58 -04:00
psychedelicious
20813b5615 fix(ui): tab hotkeys for video 2025-08-28 08:23:58 -04:00
psychedelicious
36c16d2781 chore(ui): lint 2025-08-28 08:23:58 -04:00
psychedelicious
3ae99df091 chore: ruff 2025-08-28 08:23:58 -04:00
Mary Hipp
431fd83a43 studio init action for video tab 2025-08-28 08:23:58 -04:00
Mary Hipp
ab41f71a36 launchpad cleanup 2025-08-28 08:23:58 -04:00
Mary Hipp
1f526a1c27 fix view on large screens, restore auth for screen capture 2025-08-28 08:23:58 -04:00
Mary Hipp
8a60def51f rearrange image | video | asset for boards 2025-08-28 08:23:58 -04:00
Mary Hipp
4845d31857 add option for video upsell, rearrange navigation bar and gallery tabs 2025-08-28 08:23:58 -04:00
Mary Hipp
0de5097207 hide video features if video is disabled 2025-08-28 08:23:58 -04:00
psychedelicious
505c75a5ab feat(ui): delete confirmation for videos 2025-08-28 08:23:58 -04:00
psychedelicious
4c32b2a123 feat(ui): metadata recall for videos 2025-08-28 08:23:58 -04:00
psychedelicious
b2ed3c99d4 fix(ui): do not store whole model config in state 2025-08-28 08:23:58 -04:00
psychedelicious
8eb3f40e1b fix(ui): do not change canvas bbox on video model change 2025-08-28 08:23:58 -04:00
psychedelicious
9fcba3b876 feat(ui): use correct model config object in video graph builders 2025-08-28 08:23:58 -04:00
psychedelicious
5cabc37a87 feat(ui): add selector to get model config for current video model 2025-08-28 08:23:58 -04:00
psychedelicious
c5a76806c1 feat(ui): simplify and consolidate video capture logic 2025-08-28 08:23:58 -04:00
psychedelicious
bc6dd12083 fix(ui): rebase conflict 2025-08-28 08:23:58 -04:00
Mary Hipp
41e1697e79 lint again 2025-08-28 08:23:58 -04:00
Mary Hipp
378f33bc92 lint 2025-08-28 08:23:58 -04:00
Mary Hipp
1bf25fadb3 use context to track video ref so that toolbar can also save current frame 2025-08-28 08:23:58 -04:00
Mary Hipp
6a20271dba add save frame functionality 2025-08-28 08:23:58 -04:00
Mary Hipp
e36490c2ec add video_count and asset_count to boards UI 2025-08-28 08:23:58 -04:00
Mary Hipp
d4378d9f2a add asset_count to BoardDTO and split it out from image count 2025-08-28 08:23:58 -04:00
Mary Hipp
1cc6893d0d add video_count to boardDTO 2025-08-28 08:23:58 -04:00
Mary Hipp
b16d1a943d video metadata support 2025-08-28 08:23:58 -04:00
Mary Hipp
6c375b228e split out video aspect/ratio into its own components 2025-08-28 08:23:58 -04:00
Mary Hipp
23cde86bc4 updates for new model type 2025-08-28 08:23:58 -04:00
Mary Hipp
c6f2d127ef add UI support for new model type Video 2025-08-28 08:23:58 -04:00
Mary Hipp
fb0a924918 add Video as new model type 2025-08-28 08:23:58 -04:00
psychedelicious
2d9c82da85 docs(ui): add note about visual jank in gallery 2025-08-28 08:23:58 -04:00
psychedelicious
7e031e9c01 fix(ui): use correct placeholder for vidoes 2025-08-28 08:23:58 -04:00
psychedelicious
26fe937d97 fix(ui): locate in gallery, galleryview when selecting image/video 2025-08-28 08:23:58 -04:00
psychedelicious
55139bb169 chore(ui): lint 2025-08-28 08:23:58 -04:00
psychedelicious
6a7fe6668b chore(ui): dpdm 2025-08-28 08:23:58 -04:00
psychedelicious
f5fdba795a feat(ui): video dnd 2025-08-28 08:23:58 -04:00
psychedelicious
84dc4e4ea9 fix(ui): generate tab graph builder 2025-08-28 08:23:58 -04:00
psychedelicious
24f22d539f fix(ui): iterations works for video models 2025-08-28 08:23:58 -04:00
psychedelicious
89efe9c2b1 fix(ui): missing tranlsation 2025-08-28 08:23:58 -04:00
psychedelicious
fbf8aa17c8 fix(ui): fetching imageDTO for video 2025-08-28 08:23:58 -04:00
psychedelicious
e55d39a20b tidy(ui): remove unused VideoAtPosition component 2025-08-28 08:23:58 -04:00
psychedelicious
3a1cedbced feat(ui): simpler layout for video player 2025-08-28 08:23:58 -04:00
Mary Hipp
3d9889e272 fix video styling 2025-08-28 08:23:58 -04:00
Mary Hipp
b2026d9c00 add runway back as a model and allow runway and veo3 to live together in peace and harmony 2025-08-28 08:23:58 -04:00
Mary Hipp
f631b5178f add runway to backend 2025-08-28 08:23:58 -04:00
Mary Hipp
8df3067599 update redux selection to have a list of images and/or videos, update image viewer to show either image or video depending on what is selected 2025-08-28 08:23:58 -04:00
Mary Hipp
b377b80446 lint 2025-08-28 08:23:58 -04:00
Mary Hipp
7828102b67 tsc 2025-08-28 08:23:58 -04:00
Mary Hipp
1b0d599dc2 lint the dang thing 2025-08-28 08:23:58 -04:00
psychedelicious
aa4e3adadb gallery 2025-08-28 08:23:58 -04:00
psychedelicious
637d19c22b Revert "feat(ui): consolidated gallery (wip)"
This reverts commit 12b70bca67.
2025-08-28 08:23:58 -04:00
Mary Hipp
45b4432833 add videos to change board modal 2025-08-28 08:23:58 -04:00
Mary Hipp
b71829a827 add resolution as a generation setting 2025-08-28 08:23:58 -04:00
Mary Hipp
d95a698ebd replace runway with veo, build out veo3 model support 2025-08-28 08:23:58 -04:00
Mary Hipp
49d569ec59 add Veo3 model support to backend 2025-08-28 08:23:58 -04:00
psychedelicious
6ef1c2a5e1 feat(ui): consolidated gallery (wip) 2025-08-28 08:23:58 -04:00
psychedelicious
0ec6d33086 feat(ui): gallery optimistic updates for video 2025-08-28 08:23:58 -04:00
psychedelicious
64dfa125d2 fix(ui): panel names on video tab 2025-08-28 08:23:58 -04:00
Mary Hipp
67042e6dec stubbing out change board functionality 2025-08-28 08:23:58 -04:00
Mary Hipp
a918198d4f hook up starring, unstarring, and deleting single videos (no multiselect yet), adapt context menus to work for both images and videos and start on video context menu 2025-08-28 08:23:58 -04:00
Mary Hipp
288ac0a293 add readiness logic to video tab 2025-08-28 08:23:58 -04:00
psychedelicious
963c2ec60c feat(ui): more video stuff 2025-08-28 08:23:58 -04:00
psychedelicious
79e8482b27 feat(ui): fiddle w/ video stuff 2025-08-28 08:23:58 -04:00
psychedelicious
f98bbc32dd feat(ui): fiddle w/ video stuff 2025-08-28 08:23:58 -04:00
psychedelicious
9380d8901c chore: ruff 2025-08-28 08:23:58 -04:00
psychedelicious
67de3f2d9b feat(nodes): update VideoField & VideoOutput 2025-08-28 08:23:58 -04:00
psychedelicious
530d20c1be feat(ui): add dnd target for video start frame 2025-08-28 08:23:58 -04:00
Mary Hipp
4d8bcad15b add duration and aspect ratio to video settings 2025-08-28 08:23:58 -04:00
Mary Hipp
5c93e53195 integrating video into gallery - thinking maybe a new category of image would make more senes 2025-08-28 08:23:58 -04:00
Mary Hipp
e9c4e12454 add noop video router 2025-08-28 08:23:58 -04:00
Mary Hipp
295b5a20a8 add video models 2025-08-28 08:23:58 -04:00
Mary Hipp
eff9c7b92f combine nodes that generate and save videos 2025-08-28 08:23:58 -04:00
Mary Hipp
07565d4015 build out adhoc video saving graph 2025-08-28 08:23:58 -04:00
Mary Hipp
94ba840948 push up updates for VideoField 2025-08-28 08:23:58 -04:00
Mary Hipp
bd251f8cce update VideoField 2025-08-28 08:23:58 -04:00
Mary Hipp
97719b0aab split out RunwayVideoOutput from VideoOutput 2025-08-28 08:23:58 -04:00
Mary Hipp
e89266bfe3 rough rough POC of video tab 2025-08-28 08:23:58 -04:00
Mary Hipp
453ef1a220 video_output support 2025-08-28 08:23:58 -04:00
257 changed files with 10967 additions and 4356 deletions

View File

@@ -18,5 +18,6 @@
- [ ] _The PR has a short but descriptive title, suitable for a changelog_
- [ ] _Tests added / updated (if applicable)_
- [ ] _❗Changes to a redux slice have a corresponding migration_
- [ ] _Documentation added / updated (if applicable)_
- [ ] _Updated `What's New` copy (if doing a release after this PR)_

View File

@@ -0,0 +1,39 @@
from fastapi import Body, HTTPException
from fastapi.routing import APIRouter
from invokeai.app.services.videos_common import AddVideosToBoardResult, RemoveVideosFromBoardResult
board_videos_router = APIRouter(prefix="/v1/board_videos", tags=["boards"])
@board_videos_router.post(
"/batch",
operation_id="add_videos_to_board",
responses={
201: {"description": "Videos were added to board successfully"},
},
status_code=201,
response_model=AddVideosToBoardResult,
)
async def add_videos_to_board(
board_id: str = Body(description="The id of the board to add to"),
video_ids: list[str] = Body(description="The ids of the videos to add", embed=True),
) -> AddVideosToBoardResult:
"""Adds a list of videos to a board"""
raise HTTPException(status_code=501, detail="Not implemented")
@board_videos_router.post(
"/batch/delete",
operation_id="remove_videos_from_board",
responses={
201: {"description": "Videos were removed from board successfully"},
},
status_code=201,
response_model=RemoveVideosFromBoardResult,
)
async def remove_videos_from_board(
video_ids: list[str] = Body(description="The ids of the videos to remove", embed=True),
) -> RemoveVideosFromBoardResult:
"""Removes a list of videos from their board, if they had one"""
raise HTTPException(status_code=501, detail="Not implemented")

View File

@@ -0,0 +1,119 @@
from typing import Optional
from fastapi import Body, HTTPException, Path, Query
from fastapi.routing import APIRouter
from invokeai.app.services.shared.pagination import OffsetPaginatedResults
from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection
from invokeai.app.services.videos_common import (
DeleteVideosResult,
StarredVideosResult,
UnstarredVideosResult,
VideoDTO,
VideoIdsResult,
VideoRecordChanges,
)
videos_router = APIRouter(prefix="/v1/videos", tags=["videos"])
@videos_router.patch(
"/i/{video_id}",
operation_id="update_video",
response_model=VideoDTO,
)
async def update_video(
video_id: str = Path(description="The id of the video to update"),
video_changes: VideoRecordChanges = Body(description="The changes to apply to the video"),
) -> VideoDTO:
"""Updates a video"""
raise HTTPException(status_code=501, detail="Not implemented")
@videos_router.get(
"/i/{video_id}",
operation_id="get_video_dto",
response_model=VideoDTO,
)
async def get_video_dto(
video_id: str = Path(description="The id of the video to get"),
) -> VideoDTO:
"""Gets a video's DTO"""
raise HTTPException(status_code=501, detail="Not implemented")
@videos_router.post("/delete", operation_id="delete_videos_from_list", response_model=DeleteVideosResult)
async def delete_videos_from_list(
video_ids: list[str] = Body(description="The list of ids of videos to delete", embed=True),
) -> DeleteVideosResult:
raise HTTPException(status_code=501, detail="Not implemented")
@videos_router.post("/star", operation_id="star_videos_in_list", response_model=StarredVideosResult)
async def star_videos_in_list(
video_ids: list[str] = Body(description="The list of ids of videos to star", embed=True),
) -> StarredVideosResult:
raise HTTPException(status_code=501, detail="Not implemented")
@videos_router.post("/unstar", operation_id="unstar_videos_in_list", response_model=UnstarredVideosResult)
async def unstar_videos_in_list(
video_ids: list[str] = Body(description="The list of ids of videos to unstar", embed=True),
) -> UnstarredVideosResult:
raise HTTPException(status_code=501, detail="Not implemented")
@videos_router.delete("/uncategorized", operation_id="delete_uncategorized_videos", response_model=DeleteVideosResult)
async def delete_uncategorized_videos() -> DeleteVideosResult:
"""Deletes all videos that are uncategorized"""
raise HTTPException(status_code=501, detail="Not implemented")
@videos_router.get("/", operation_id="list_video_dtos", response_model=OffsetPaginatedResults[VideoDTO])
async def list_video_dtos(
is_intermediate: Optional[bool] = Query(default=None, description="Whether to list intermediate videos."),
board_id: Optional[str] = Query(
default=None,
description="The board id to filter by. Use 'none' to find videos without a board.",
),
offset: int = Query(default=0, description="The page offset"),
limit: int = Query(default=10, description="The number of videos per page"),
order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending, description="The order of sort"),
starred_first: bool = Query(default=True, description="Whether to sort by starred videos first"),
search_term: Optional[str] = Query(default=None, description="The term to search for"),
) -> OffsetPaginatedResults[VideoDTO]:
"""Lists video DTOs"""
raise HTTPException(status_code=501, detail="Not implemented")
@videos_router.get("/ids", operation_id="get_video_ids")
async def get_video_ids(
is_intermediate: Optional[bool] = Query(default=None, description="Whether to list intermediate videos."),
board_id: Optional[str] = Query(
default=None,
description="The board id to filter by. Use 'none' to find videos without a board.",
),
order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending, description="The order of sort"),
starred_first: bool = Query(default=True, description="Whether to sort by starred videos first"),
search_term: Optional[str] = Query(default=None, description="The term to search for"),
) -> VideoIdsResult:
"""Gets ordered list of video ids with metadata for optimistic updates"""
raise HTTPException(status_code=501, detail="Not implemented")
@videos_router.post(
"/videos_by_ids",
operation_id="get_videos_by_ids",
responses={200: {"model": list[VideoDTO]}},
)
async def get_videos_by_ids(
video_ids: list[str] = Body(embed=True, description="Object containing list of video ids to fetch DTOs for"),
) -> list[VideoDTO]:
"""Gets video DTOs for the specified video ids. Maintains order of input ids."""
raise HTTPException(status_code=501, detail="Not implemented")

View File

@@ -18,6 +18,7 @@ from invokeai.app.api.no_cache_staticfiles import NoCacheStaticFiles
from invokeai.app.api.routers import (
app_info,
board_images,
board_videos,
boards,
client_state,
download_queue,
@@ -27,6 +28,7 @@ from invokeai.app.api.routers import (
session_queue,
style_presets,
utilities,
videos,
workflows,
)
from invokeai.app.api.sockets import SocketIO
@@ -125,8 +127,10 @@ app.include_router(utilities.utilities_router, prefix="/api")
app.include_router(model_manager.model_manager_router, prefix="/api")
app.include_router(download_queue.download_queue_router, prefix="/api")
app.include_router(images.images_router, prefix="/api")
app.include_router(videos.videos_router, prefix="/api")
app.include_router(boards.boards_router, prefix="/api")
app.include_router(board_images.board_images_router, prefix="/api")
app.include_router(board_videos.board_videos_router, prefix="/api")
app.include_router(model_relationships.model_relationships_router, prefix="/api")
app.include_router(app_info.app_router, prefix="/api")
app.include_router(session_queue.session_queue_router, prefix="/api")

View File

@@ -66,11 +66,14 @@ class UIType(str, Enum, metaclass=MetaEnum):
ChatGPT4oModel = "ChatGPT4oModelField"
Gemini2_5Model = "Gemini2_5ModelField"
FluxKontextModel = "FluxKontextModelField"
Veo3Model = "Veo3ModelField"
RunwayModel = "RunwayModelField"
# endregion
# region Misc Field Types
Scheduler = "SchedulerField"
Any = "AnyField"
Video = "VideoField"
# endregion
# region Internal Field Types
@@ -225,6 +228,12 @@ class ImageField(BaseModel):
image_name: str = Field(description="The name of the image")
class VideoField(BaseModel):
"""A video primitive field"""
video_id: str = Field(description="The id of the video")
class BoardField(BaseModel):
"""A board primitive field"""

View File

@@ -27,6 +27,7 @@ from invokeai.app.invocations.fields import (
SD3ConditioningField,
TensorField,
UIComponent,
VideoField,
)
from invokeai.app.services.images.images_common import ImageDTO
from invokeai.app.services.shared.invocation_context import InvocationContext
@@ -287,6 +288,30 @@ class ImageCollectionInvocation(BaseInvocation):
return ImageCollectionOutput(collection=self.collection)
# endregion
# region Video
@invocation_output("video_output")
class VideoOutput(BaseInvocationOutput):
"""Base class for nodes that output a video"""
video: VideoField = OutputField(description="The output video")
width: int = OutputField(description="The width of the video in pixels")
height: int = OutputField(description="The height of the video in pixels")
duration_seconds: float = OutputField(description="The duration of the video in seconds")
@classmethod
def build(cls, video_id: str, width: int, height: int, duration_seconds: float) -> "VideoOutput":
return cls(
video=VideoField(video_id=video_id),
width=width,
height=height,
duration_seconds=duration_seconds,
)
# endregion
# region DenoiseMask

View File

@@ -49,3 +49,11 @@ class BoardImageRecordStorageBase(ABC):
) -> int:
"""Gets the number of images for a board."""
pass
@abstractmethod
def get_asset_count_for_board(
self,
board_id: str,
) -> int:
"""Gets the number of assets for a board."""
pass

View File

@@ -3,6 +3,8 @@ from typing import Optional, cast
from invokeai.app.services.board_image_records.board_image_records_base import BoardImageRecordStorageBase
from invokeai.app.services.image_records.image_records_common import (
ASSETS_CATEGORIES,
IMAGE_CATEGORIES,
ImageCategory,
ImageRecord,
deserialize_image_record,
@@ -151,15 +153,38 @@ class SqliteBoardImageRecordStorage(BoardImageRecordStorageBase):
def get_image_count_for_board(self, board_id: str) -> int:
with self._db.transaction() as cursor:
# Convert the enum values to unique list of strings
category_strings = [c.value for c in set(IMAGE_CATEGORIES)]
# Create the correct length of placeholders
placeholders = ",".join("?" * len(category_strings))
cursor.execute(
"""--sql
f"""--sql
SELECT COUNT(*)
FROM board_images
INNER JOIN images ON board_images.image_name = images.image_name
WHERE images.is_intermediate = FALSE
WHERE images.is_intermediate = FALSE AND images.image_category IN ( {placeholders} )
AND board_images.board_id = ?;
""",
(board_id,),
(*category_strings, board_id),
)
count = cast(int, cursor.fetchone()[0])
return count
def get_asset_count_for_board(self, board_id: str) -> int:
with self._db.transaction() as cursor:
# Convert the enum values to unique list of strings
category_strings = [c.value for c in set(ASSETS_CATEGORIES)]
# Create the correct length of placeholders
placeholders = ",".join("?" * len(category_strings))
cursor.execute(
f"""--sql
SELECT COUNT(*)
FROM board_images
INNER JOIN images ON board_images.image_name = images.image_name
WHERE images.is_intermediate = FALSE AND images.image_category IN ( {placeholders} )
AND board_images.board_id = ?;
""",
(*category_strings, board_id),
)
count = cast(int, cursor.fetchone()[0])
return count

View File

@@ -12,12 +12,20 @@ class BoardDTO(BoardRecord):
"""The URL of the thumbnail of the most recent image in the board."""
image_count: int = Field(description="The number of images in the board.")
"""The number of images in the board."""
asset_count: int = Field(description="The number of assets in the board.")
"""The number of assets in the board."""
video_count: int = Field(description="The number of videos in the board.")
"""The number of videos in the board."""
def board_record_to_dto(board_record: BoardRecord, cover_image_name: Optional[str], image_count: int) -> BoardDTO:
def board_record_to_dto(
board_record: BoardRecord, cover_image_name: Optional[str], image_count: int, asset_count: int, video_count: int
) -> BoardDTO:
"""Converts a board record to a board DTO."""
return BoardDTO(
**board_record.model_dump(exclude={"cover_image_name"}),
cover_image_name=cover_image_name,
image_count=image_count,
asset_count=asset_count,
video_count=video_count,
)

View File

@@ -17,7 +17,7 @@ class BoardService(BoardServiceABC):
board_name: str,
) -> BoardDTO:
board_record = self.__invoker.services.board_records.save(board_name)
return board_record_to_dto(board_record, None, 0)
return board_record_to_dto(board_record, None, 0, 0, 0)
def get_dto(self, board_id: str) -> BoardDTO:
board_record = self.__invoker.services.board_records.get(board_id)
@@ -27,7 +27,9 @@ class BoardService(BoardServiceABC):
else:
cover_image_name = None
image_count = self.__invoker.services.board_image_records.get_image_count_for_board(board_id)
return board_record_to_dto(board_record, cover_image_name, image_count)
asset_count = self.__invoker.services.board_image_records.get_asset_count_for_board(board_id)
video_count = 0 # noop for OSS
return board_record_to_dto(board_record, cover_image_name, image_count, asset_count, video_count)
def update(
self,
@@ -42,7 +44,9 @@ class BoardService(BoardServiceABC):
cover_image_name = None
image_count = self.__invoker.services.board_image_records.get_image_count_for_board(board_id)
return board_record_to_dto(board_record, cover_image_name, image_count)
asset_count = self.__invoker.services.board_image_records.get_asset_count_for_board(board_id)
video_count = 0 # noop for OSS
return board_record_to_dto(board_record, cover_image_name, image_count, asset_count, video_count)
def delete(self, board_id: str) -> None:
self.__invoker.services.board_records.delete(board_id)
@@ -67,7 +71,9 @@ class BoardService(BoardServiceABC):
cover_image_name = None
image_count = self.__invoker.services.board_image_records.get_image_count_for_board(r.board_id)
board_dtos.append(board_record_to_dto(r, cover_image_name, image_count))
asset_count = self.__invoker.services.board_image_records.get_asset_count_for_board(r.board_id)
video_count = 0 # noop for OSS
board_dtos.append(board_record_to_dto(r, cover_image_name, image_count, asset_count, video_count))
return OffsetPaginatedResults[BoardDTO](items=board_dtos, offset=offset, limit=limit, total=len(board_dtos))
@@ -84,6 +90,8 @@ class BoardService(BoardServiceABC):
cover_image_name = None
image_count = self.__invoker.services.board_image_records.get_image_count_for_board(r.board_id)
board_dtos.append(board_record_to_dto(r, cover_image_name, image_count))
asset_count = self.__invoker.services.board_image_records.get_asset_count_for_board(r.board_id)
video_count = 0 # noop for OSS
board_dtos.append(board_record_to_dto(r, cover_image_name, image_count, asset_count, video_count))
return board_dtos

View File

@@ -58,6 +58,15 @@ class ImageCategory(str, Enum, metaclass=MetaEnum):
"""OTHER: The image is some other type of image with a specialized purpose. To be used by external nodes."""
IMAGE_CATEGORIES: list[ImageCategory] = [ImageCategory.GENERAL]
ASSETS_CATEGORIES: list[ImageCategory] = [
ImageCategory.CONTROL,
ImageCategory.MASK,
ImageCategory.USER,
ImageCategory.OTHER,
]
class InvalidImageCategoryException(ValueError):
"""Raised when a provided value is not a valid ImageCategory.

View File

@@ -15,6 +15,7 @@ from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
from invokeai.backend.model_manager.config import (
AnyModelConfig,
ControlAdapterDefaultSettings,
LoraModelDefaultSettings,
MainModelDefaultSettings,
)
from invokeai.backend.model_manager.taxonomy import (
@@ -83,8 +84,8 @@ class ModelRecordChanges(BaseModelExcludeNull):
file_size: Optional[int] = Field(description="Size of model file", default=None)
format: Optional[str] = Field(description="format of model file", default=None)
trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None)
default_settings: Optional[MainModelDefaultSettings | ControlAdapterDefaultSettings] = Field(
description="Default settings for this model", default=None
default_settings: Optional[MainModelDefaultSettings | LoraModelDefaultSettings | ControlAdapterDefaultSettings] = (
Field(description="Default settings for this model", default=None)
)
# Checkpoint-specific changes

View File

@@ -0,0 +1,179 @@
import datetime
from typing import Optional, Union
from pydantic import BaseModel, Field, StrictBool, StrictStr
from invokeai.app.util.misc import get_iso_timestamp
from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
VIDEO_DTO_COLS = ", ".join(
[
"videos." + c
for c in [
"video_id",
"width",
"height",
"session_id",
"node_id",
"is_intermediate",
"created_at",
"updated_at",
"deleted_at",
"starred",
]
]
)
class VideoRecord(BaseModelExcludeNull):
"""Deserialized video record without metadata."""
video_id: str = Field(description="The unique id of the video.")
"""The unique id of the video."""
width: int = Field(description="The width of the video in px.")
"""The actual width of the video in px. This may be different from the width in metadata."""
height: int = Field(description="The height of the video in px.")
"""The actual height of the video in px. This may be different from the height in metadata."""
created_at: Union[datetime.datetime, str] = Field(description="The created timestamp of the video.")
"""The created timestamp of the video."""
updated_at: Union[datetime.datetime, str] = Field(description="The updated timestamp of the video.")
"""The updated timestamp of the video."""
deleted_at: Optional[Union[datetime.datetime, str]] = Field(
default=None, description="The deleted timestamp of the video."
)
"""The deleted timestamp of the video."""
is_intermediate: bool = Field(description="Whether this is an intermediate video.")
"""Whether this is an intermediate video."""
session_id: Optional[str] = Field(
default=None,
description="The session ID that generated this video, if it is a generated video.",
)
"""The session ID that generated this video, if it is a generated video."""
node_id: Optional[str] = Field(
default=None,
description="The node ID that generated this video, if it is a generated video.",
)
"""The node ID that generated this video, if it is a generated video."""
starred: bool = Field(description="Whether this video is starred.")
"""Whether this video is starred."""
class VideoRecordChanges(BaseModelExcludeNull):
"""A set of changes to apply to a video record.
Only limited changes are valid:
- `session_id`: change the session associated with a video
- `is_intermediate`: change the video's `is_intermediate` flag
- `starred`: change whether the video is starred
"""
session_id: Optional[StrictStr] = Field(
default=None,
description="The video's new session ID.",
)
"""The video's new session ID."""
is_intermediate: Optional[StrictBool] = Field(default=None, description="The video's new `is_intermediate` flag.")
"""The video's new `is_intermediate` flag."""
starred: Optional[StrictBool] = Field(default=None, description="The video's new `starred` state")
"""The video's new `starred` state."""
def deserialize_video_record(video_dict: dict) -> VideoRecord:
"""Deserializes a video record."""
# Retrieve all the values, setting "reasonable" defaults if they are not present.
video_id = video_dict.get("video_id", "unknown")
width = video_dict.get("width", 0)
height = video_dict.get("height", 0)
session_id = video_dict.get("session_id", None)
node_id = video_dict.get("node_id", None)
created_at = video_dict.get("created_at", get_iso_timestamp())
updated_at = video_dict.get("updated_at", get_iso_timestamp())
deleted_at = video_dict.get("deleted_at", get_iso_timestamp())
is_intermediate = video_dict.get("is_intermediate", False)
starred = video_dict.get("starred", False)
return VideoRecord(
video_id=video_id,
width=width,
height=height,
session_id=session_id,
node_id=node_id,
created_at=created_at,
updated_at=updated_at,
deleted_at=deleted_at,
is_intermediate=is_intermediate,
starred=starred,
)
class VideoCollectionCounts(BaseModel):
starred_count: int = Field(description="The number of starred videos in the collection.")
unstarred_count: int = Field(description="The number of unstarred videos in the collection.")
class VideoIdsResult(BaseModel):
"""Response containing ordered video ids with metadata for optimistic updates."""
video_ids: list[str] = Field(description="Ordered list of video ids")
starred_count: int = Field(description="Number of starred videos (when starred_first=True)")
total_count: int = Field(description="Total number of videos matching the query")
class VideoUrlsDTO(BaseModelExcludeNull):
"""The URLs for an image and its thumbnail."""
video_id: str = Field(description="The unique id of the video.")
"""The unique id of the video."""
video_url: str = Field(description="The URL of the video.")
"""The URL of the video."""
thumbnail_url: str = Field(description="The URL of the video's thumbnail.")
"""The URL of the video's thumbnail."""
class VideoDTO(VideoRecord, VideoUrlsDTO):
"""Deserialized video record, enriched for the frontend."""
board_id: Optional[str] = Field(
default=None, description="The id of the board the image belongs to, if one exists."
)
"""The id of the board the image belongs to, if one exists."""
def video_record_to_dto(
video_record: VideoRecord,
video_url: str,
thumbnail_url: str,
board_id: Optional[str],
) -> VideoDTO:
"""Converts a video record to a video DTO."""
return VideoDTO(
**video_record.model_dump(),
video_url=video_url,
thumbnail_url=thumbnail_url,
board_id=board_id,
)
class ResultWithAffectedBoards(BaseModel):
affected_boards: list[str] = Field(description="The ids of boards affected by the delete operation")
class DeleteVideosResult(ResultWithAffectedBoards):
deleted_videos: list[str] = Field(description="The ids of the videos that were deleted")
class StarredVideosResult(ResultWithAffectedBoards):
starred_videos: list[str] = Field(description="The ids of the videos that were starred")
class UnstarredVideosResult(ResultWithAffectedBoards):
unstarred_videos: list[str] = Field(description="The ids of the videos that were unstarred")
class AddVideosToBoardResult(ResultWithAffectedBoards):
added_videos: list[str] = Field(description="The video ids that were added to the board")
class RemoveVideosFromBoardResult(ResultWithAffectedBoards):
removed_videos: list[str] = Field(description="The video ids that were removed from their board")

View File

@@ -90,6 +90,11 @@ class MainModelDefaultSettings(BaseModel):
model_config = ConfigDict(extra="forbid")
class LoraModelDefaultSettings(BaseModel):
weight: float | None = Field(default=None, ge=-1, le=2, description="Default weight for this model")
model_config = ConfigDict(extra="forbid")
class ControlAdapterDefaultSettings(BaseModel):
# This could be narrowed to controlnet processor nodes, but they change. Leaving this a string is safer.
preprocessor: str | None
@@ -287,6 +292,9 @@ class LoRAConfigBase(ABC, BaseModel):
type: Literal[ModelType.LoRA] = ModelType.LoRA
trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None)
default_settings: Optional[LoraModelDefaultSettings] = Field(
description="Default settings for this model", default=None
)
@classmethod
def flux_lora_format(cls, mod: ModelOnDisk):
@@ -492,6 +500,15 @@ class MainConfigBase(ABC, BaseModel):
variant: AnyVariant = ModelVariantType.Normal
class VideoConfigBase(ABC, BaseModel):
type: Literal[ModelType.Video] = ModelType.Video
trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None)
default_settings: Optional[MainModelDefaultSettings] = Field(
description="Default settings for this model", default=None
)
variant: AnyVariant = ModelVariantType.Normal
class MainCheckpointConfig(CheckpointConfigBase, MainConfigBase, LegacyProbeMixin, ModelConfigBase):
"""Model config for main checkpoint models."""
@@ -649,6 +666,21 @@ class ApiModelConfig(MainConfigBase, ModelConfigBase):
raise NotImplementedError("API models are not parsed from disk.")
class VideoApiModelConfig(VideoConfigBase, ModelConfigBase):
"""Model config for API-based video models."""
format: Literal[ModelFormat.Api] = ModelFormat.Api
@classmethod
def matches(cls, mod: ModelOnDisk) -> bool:
# API models are not stored on disk, so we can't match them.
return False
@classmethod
def parse(cls, mod: ModelOnDisk) -> dict[str, Any]:
raise NotImplementedError("API models are not parsed from disk.")
def get_model_discriminator_value(v: Any) -> str:
"""
Computes the discriminator value for a model config.
@@ -718,12 +750,13 @@ AnyModelConfig = Annotated[
Annotated[FluxReduxConfig, FluxReduxConfig.get_tag()],
Annotated[LlavaOnevisionConfig, LlavaOnevisionConfig.get_tag()],
Annotated[ApiModelConfig, ApiModelConfig.get_tag()],
Annotated[VideoApiModelConfig, VideoApiModelConfig.get_tag()],
],
Discriminator(get_model_discriminator_value),
]
AnyModelConfigValidator = TypeAdapter(AnyModelConfig)
AnyDefaultSettings: TypeAlias = Union[MainModelDefaultSettings, ControlAdapterDefaultSettings]
AnyDefaultSettings: TypeAlias = Union[MainModelDefaultSettings, LoraModelDefaultSettings, ControlAdapterDefaultSettings]
class ModelConfigFactory:

View File

@@ -23,6 +23,7 @@ from invokeai.backend.model_manager.config import (
AnyModelConfig,
ControlAdapterDefaultSettings,
InvalidModelConfigException,
LoraModelDefaultSettings,
MainModelDefaultSettings,
ModelConfigFactory,
SubmodelDefinition,
@@ -217,6 +218,8 @@ class ModelProbe(object):
if not fields["default_settings"]:
if fields["type"] in {ModelType.ControlNet, ModelType.T2IAdapter, ModelType.ControlLoRa}:
fields["default_settings"] = get_default_settings_control_adapters(fields["name"])
if fields["type"] in {ModelType.LoRA}:
fields["default_settings"] = get_default_settings_lora()
elif fields["type"] is ModelType.Main:
fields["default_settings"] = get_default_settings_main(fields["base"])
@@ -543,6 +546,10 @@ def get_default_settings_control_adapters(model_name: str) -> Optional[ControlAd
return None
def get_default_settings_lora() -> LoraModelDefaultSettings:
return LoraModelDefaultSettings()
def get_default_settings_main(model_base: BaseModelType) -> Optional[MainModelDefaultSettings]:
if model_base is BaseModelType.StableDiffusion1 or model_base is BaseModelType.StableDiffusion2:
return MainModelDefaultSettings(width=512, height=512)

View File

@@ -31,6 +31,8 @@ class BaseModelType(str, Enum):
Gemini2_5 = "gemini-2.5"
ChatGPT4o = "chatgpt-4o"
FluxKontext = "flux-kontext"
Veo3 = "veo3"
Runway = "runway"
class ModelType(str, Enum):
@@ -52,6 +54,7 @@ class ModelType(str, Enum):
SigLIP = "siglip"
FluxRedux = "flux_redux"
LlavaOnevision = "llava_onevision"
Video = "video"
class SubModelType(str, Enum):

View File

@@ -56,7 +56,7 @@
"chakra-react-select": "^4.9.2",
"cmdk": "^1.1.1",
"compare-versions": "^6.1.1",
"dockview": "^4.4.1",
"dockview": "^4.7.1",
"es-toolkit": "^1.39.7",
"filesize": "^10.1.6",
"fracturedjsonjs": "^4.1.0",
@@ -69,6 +69,7 @@
"linkify-react": "^4.3.1",
"linkifyjs": "^4.3.1",
"lru-cache": "^11.1.0",
"media-chrome": "^4.13.0",
"mtwist": "^1.0.2",
"nanoid": "^5.1.5",
"nanostores": "^1.0.1",
@@ -87,6 +88,7 @@
"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

@@ -60,8 +60,8 @@ importers:
specifier: ^6.1.1
version: 6.1.1
dockview:
specifier: ^4.4.1
version: 4.4.1(react@18.3.1)
specifier: ^4.7.1
version: 4.7.1(react@18.3.1)
es-toolkit:
specifier: ^1.39.7
version: 1.39.7
@@ -98,6 +98,9 @@ importers:
lru-cache:
specifier: ^11.1.0
version: 11.1.0
media-chrome:
specifier: ^4.13.0
version: 4.13.0(react@18.3.1)
mtwist:
specifier: ^1.0.2
version: 1.0.2
@@ -152,6 +155,9 @@ 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)
@@ -414,8 +420,8 @@ packages:
resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.28.2':
resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==}
'@babel/runtime@7.28.3':
resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==}
engines: {node: '>=6.9.0'}
'@babel/template@7.27.2':
@@ -937,6 +943,31 @@ 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}
@@ -1453,6 +1484,9 @@ 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'}
@@ -1707,6 +1741,12 @@ 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:
@@ -1959,6 +1999,15 @@ 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'}
@@ -2014,6 +2063,14 @@ 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'}
@@ -2064,12 +2121,18 @@ 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'}
@@ -2139,6 +2202,9 @@ 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'}
@@ -2177,6 +2243,12 @@ 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'}
@@ -2254,11 +2326,11 @@ packages:
discontinuous-range@1.0.0:
resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==}
dockview-core@4.4.1:
resolution: {integrity: sha512-pDQPlVfDYDuN3zSebVUMVn2x21bpYPGD1ybGYrKJMI1KDkSQSqy57FJRJXi7yEnkcrmBUF0xEEo4d0Yi3j2vGA==}
dockview-core@4.7.1:
resolution: {integrity: sha512-Tia3vYHtqACMZTiZv86yQOabwKj5KrBhQqlSr7qXV0qmmRSZ8dNbaU63LIHYFprST7JgHupIm9JVES+OhqMoTQ==}
dockview@4.4.1:
resolution: {integrity: sha512-XEAwl+VYVZGkBd3hprF6kRLspWSF/hydbRHuV3KEg3BHev1i5xc+H+Kjp+u5DHTQ97klFAATl5MntNoVXQeg0w==}
dockview@4.7.1:
resolution: {integrity: sha512-DgMzSKNjDvZzIQjFfAV6I6EDkqe40Sjz1Qgyf88KG4U1Kgp/bIIEDSLpz65BsW5ZD9Qi3y18TCISYTgsNvU9TA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
@@ -2751,9 +2823,18 @@ 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==}
@@ -2792,6 +2873,9 @@ 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==}
@@ -2803,6 +2887,9 @@ 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'}
@@ -2825,6 +2912,12 @@ 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'}
@@ -2860,6 +2953,9 @@ 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'}
@@ -3064,6 +3160,9 @@ 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==}
@@ -3088,6 +3187,9 @@ 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'}
@@ -3154,6 +3256,15 @@ 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-chrome@4.13.0:
resolution: {integrity: sha512-DfX/Hwxjae/tEHjr1tVnV/6XDFHriMXI1ev8Ji4Z/YwXnqMhNfRtvNsMjefnQK5pkMS/9hC+jmdS+VDWZfsSIw==}
media-tracks@0.3.3:
resolution: {integrity: sha512-9P2FuUHnZZ3iji+2RQk7Zkh5AmZTnOG5fODACnjhCVveX1McY3jmCRHofIEI+yTBqplz7LXy48c7fQ3Uigp88w==}
memoize-one@6.0.0:
resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
@@ -3219,6 +3330,12 @@ 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:
@@ -3243,6 +3360,9 @@ 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==}
@@ -3433,6 +3553,9 @@ 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'}
@@ -3575,6 +3698,13 @@ 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:
@@ -3809,6 +3939,9 @@ 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==}
@@ -3931,6 +4064,9 @@ 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==}
@@ -4039,6 +4175,9 @@ 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'}
@@ -4063,6 +4202,9 @@ 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==}
@@ -4148,6 +4290,9 @@ 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'}
@@ -4182,6 +4327,10 @@ 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==}
@@ -4286,6 +4435,9 @@ 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}
@@ -4401,6 +4553,10 @@ 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==}
@@ -4436,6 +4592,9 @@ 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'}
@@ -4509,6 +4668,9 @@ 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'}
@@ -4640,7 +4802,7 @@ snapshots:
'@babel/runtime@7.27.6': {}
'@babel/runtime@7.28.2': {}
'@babel/runtime@7.28.3': {}
'@babel/template@7.27.2':
dependencies:
@@ -5235,6 +5397,43 @@ 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
@@ -5690,6 +5889,8 @@ snapshots:
optionalDependencies:
typescript: 5.8.3
'@svta/common-media-library@0.12.4': {}
'@swc/core-darwin-arm64@1.12.9':
optional: true
@@ -5745,7 +5946,7 @@ snapshots:
'@testing-library/dom@10.4.0':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/runtime': 7.28.2
'@babel/runtime': 7.28.3
'@types/aria-query': 5.0.4
aria-query: 5.3.0
chalk: 4.1.2
@@ -5971,6 +6172,13 @@ 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
@@ -6304,6 +6512,19 @@ 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
@@ -6366,6 +6587,14 @@ 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
@@ -6423,6 +6652,8 @@ 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)
@@ -6435,6 +6666,8 @@ snapshots:
- '@types/react'
- '@types/react-dom'
codem-isoboxer@0.3.10: {}
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@@ -6510,6 +6743,8 @@ snapshots:
csstype@3.1.3: {}
custom-media-element@1.4.5: {}
d3-color@3.1.0: {}
d3-dispatch@3.0.1: {}
@@ -6546,6 +6781,24 @@ 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
@@ -6610,11 +6863,11 @@ snapshots:
discontinuous-range@1.0.0: {}
dockview-core@4.4.1: {}
dockview-core@4.7.1: {}
dockview@4.4.1(react@18.3.1):
dockview@4.7.1(react@18.3.1):
dependencies:
dockview-core: 4.4.1
dockview-core: 4.7.1
react: 18.3.1
doctrine@2.1.0:
@@ -7244,10 +7497,20 @@ 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:
@@ -7283,6 +7546,8 @@ snapshots:
ignore@7.0.5: {}
immediate@3.0.6: {}
immer@10.1.1: {}
import-fresh@3.3.1:
@@ -7292,6 +7557,10 @@ snapshots:
import-lazy@4.0.0: {}
imsc@1.1.5:
dependencies:
sax: 1.2.1
imurmurhash@0.1.4: {}
indent-string@4.0.0: {}
@@ -7310,6 +7579,13 @@ 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
@@ -7352,6 +7628,8 @@ 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: {}
@@ -7553,6 +7831,10 @@ 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):
@@ -7575,6 +7857,10 @@ 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
@@ -7634,6 +7920,21 @@ 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-chrome@4.13.0(react@18.3.1):
dependencies:
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: {}
@@ -7690,6 +7991,10 @@ 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
@@ -7711,6 +8016,8 @@ snapshots:
nanostores@1.0.1: {}
native-promise-only@0.8.1: {}
natural-compare@1.4.0: {}
nearley@2.20.1:
@@ -7929,6 +8236,12 @@ 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: {}
@@ -8066,6 +8379,24 @@ 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
@@ -8360,6 +8691,8 @@ snapshots:
safe-stable-stringify@2.5.0: {}
sax@1.2.1: {}
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
@@ -8484,6 +8817,8 @@ snapshots:
split-on-first@3.0.0: {}
spotify-audio-element@1.0.2: {}
sprintf-js@1.0.3: {}
stable-hash@0.0.6: {}
@@ -8627,6 +8962,8 @@ snapshots:
stylis@4.3.6: {}
super-media-element@1.4.2: {}
supports-color@10.0.0: {}
supports-color@7.2.0:
@@ -8647,6 +8984,8 @@ snapshots:
throttle-debounce@3.0.1: {}
tiktok-video-element@0.1.0: {}
tiny-invariant@1.3.3: {}
tinybench@2.9.0: {}
@@ -8709,6 +9048,8 @@ snapshots:
tslib@2.8.1: {}
twitch-video-element@0.1.2: {}
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
@@ -8752,6 +9093,8 @@ snapshots:
typescript@5.8.3: {}
ua-parser-js@1.0.40: {}
ufo@1.6.1: {}
unbox-primitive@1.1.0:
@@ -8834,6 +9177,10 @@ 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
@@ -8962,6 +9309,8 @@ snapshots:
dependencies:
defaults: 1.0.4
weakmap-polyfill@2.0.4: {}
webidl-conversions@3.0.1: {}
webpack-virtual-modules@0.6.2: {}
@@ -9021,6 +9370,10 @@ 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:
@@ -9067,6 +9420,8 @@ 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

@@ -14,8 +14,7 @@
"gallery": {
"galleryImageSize": "حجم الصورة",
"gallerySettings": "إعدادات المعرض",
"autoSwitchNewImages": "التبديل التلقائي إلى الصور الجديدة",
"noImagesInGallery": "لا توجد صور في المعرض"
"autoSwitchNewImages": "التبديل التلقائي إلى الصور الجديدة"
},
"modelManager": {
"modelManager": "مدير النموذج",
@@ -62,12 +61,10 @@
"infillMethod": "طريقة التعبئة",
"tileSize": "حجم البلاطة",
"copyImage": "نسخ الصورة",
"downloadImage": "تحميل الصورة",
"usePrompt": "استخدم المحث",
"useSeed": "استخدام البذور",
"useAll": "استخدام الكل",
"info": "معلومات",
"showOptionsPanel": "إظهار لوحة الخيارات"
"info": "معلومات"
},
"settings": {
"models": "موديلات",

View File

@@ -24,7 +24,6 @@
"ipAdapter": "IP Adapter",
"auto": "Auto",
"controlNet": "ControlNet",
"imageFailedToLoad": "Kann Bild nicht laden",
"modelManager": "Model Manager",
"learnMore": "Mehr erfahren",
"loading": "Lade",
@@ -52,7 +51,6 @@
"somethingWentWrong": "Etwas ist schief gelaufen",
"copyError": "$t(gallery.copy) Fehler",
"input": "Eingabe",
"notInstalled": "Nicht $t(common.installed)",
"alpha": "Alpha",
"red": "Rot",
"green": "Grün",
@@ -62,11 +60,8 @@
"direction": "Richtung",
"save": "Speichern",
"created": "Erstellt",
"prevPage": "Vorherige Seite",
"nextPage": "Nächste Seite",
"unknownError": "Unbekannter Fehler",
"aboutDesc": "Verwenden Sie Invoke für die Arbeit? Siehe hier:",
"localSystem": "Lokales System",
"orderBy": "Ordnen nach",
"saveAs": "Speichern als",
"updated": "Aktualisiert",
@@ -77,7 +72,6 @@
"selected": "Ausgewählt",
"beta": "Beta",
"editor": "Editor",
"goTo": "Gehe zu",
"positivePrompt": "Positiv-Prompt",
"negativePrompt": "Negativ-Prompt",
"tab": "Tabulator",
@@ -106,7 +100,6 @@
"values": "Werte",
"min": "Min",
"max": "Max",
"resetToDefaults": "Auf Standard zurücksetzen",
"seed": "Seed",
"row": "Reihe",
"column": "Spalte",
@@ -135,14 +128,12 @@
"galleryImageSize": "Bildgröße",
"gallerySettings": "Galerie-Einstellungen",
"autoSwitchNewImages": "Auto-Wechsel zu neuen Bildern",
"noImagesInGallery": "Keine Bilder in der Galerie",
"loading": "Lade",
"deleteImage_one": "Lösche Bild",
"deleteImage_other": "Lösche {{count}} Bilder",
"copy": "Kopieren",
"download": "Runterladen",
"featuresWillReset": "Wenn Sie dieses Bild löschen, werden diese Funktionen sofort zurückgesetzt.",
"unableToLoad": "Galerie kann nicht geladen werden",
"downloadSelection": "Auswahl herunterladen",
"currentlyInUse": "Dieses Bild wird derzeit in den folgenden Funktionen verwendet:",
"deleteImagePermanent": "Gelöschte Bilder können nicht wiederhergestellt werden.",
@@ -182,16 +173,12 @@
"gallery": "Galerie",
"sortDirection": "Sortierreihenfolge",
"sideBySide": "Nebeneinander",
"openViewer": "Viewer öffnen",
"viewerImage": "Viewer-Bild",
"exitCompare": "Vergleichen beenden",
"closeViewer": "Viewer schließen",
"selectAnImageToCompare": "Wählen Sie ein Bild zum Vergleichen",
"stretchToFit": "Strecken bis es passt",
"displayBoardSearch": "Board durchsuchen",
"displaySearch": "Bild suchen",
"go": "Los",
"jump": "Springen",
"assetsTab": "Dateien, die Sie zur Verwendung in Ihren Projekten hochgeladen haben.",
"imagesTab": "Bilder, die Sie in Invoke erstellt und gespeichert haben.",
"boardsSettings": "Ordnereinstellungen",
@@ -210,10 +197,6 @@
"title": "Bbox Werkzeug",
"desc": "Bbox Werkzeug auswählen."
},
"setFillToWhite": {
"title": "Farbe auf Weiß einstellen",
"desc": "Setzt die aktuelle Werkzeugfarbe auf weiß."
},
"title": "Leinwand",
"selectBrushTool": {
"title": "Pinselwerkzeug",
@@ -578,7 +561,6 @@
"urlOrLocalPath": "URL oder lokaler Pfad",
"install": "Installieren",
"textualInversions": "Textuelle Inversionen",
"ipAdapters": "IP-Adapter",
"modelImageUpdated": "Modellbild aktualisiert",
"path": "Pfad",
"pathToConfig": "Pfad zur Konfiguration",
@@ -601,7 +583,6 @@
"repoVariant": "Repo Variante",
"learnMoreAboutSupportedModels": "Erfahren Sie mehr über die Modelle, die wir unterstützen",
"clipEmbed": "CLIP einbetten",
"starterModelsInModelManager": "Modelle für Ihren Start finden Sie im Modell-Manager",
"noModelsInstalledDesc1": "Installiere Modelle mit dem",
"modelImageUpdateFailed": "Modellbild-Update fehlgeschlagen",
"prune": "Bereinigen",
@@ -661,11 +642,9 @@
"scaledHeight": "Skaliert H",
"infillMethod": "Infill-Methode",
"tileSize": "Kachelgröße",
"downloadImage": "Bild herunterladen",
"usePrompt": "Prompt verwenden",
"useSeed": "Seed verwenden",
"useAll": "Alle verwenden",
"showOptionsPanel": "Optionsleiste zeigen",
"copyImage": "Bild kopieren",
"denoisingStrength": "Stärke der Entrauschung",
"symmetry": "Symmetrie",
@@ -681,10 +660,6 @@
"remixImage": "Remix des Bilds erstellen",
"imageActions": "Weitere Bildaktionen",
"invoke": {
"fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), Skalierte Bbox-Breite ist {{width}}",
"fluxModelIncompatibleScaledBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), Skalierte Bbox-Höhe ist {{height}}",
"fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), Bbox-Breite ist {{width}}",
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), Bbox-Höhe ist {{height}}",
"noNodesInGraph": "Keine Knoten im Graphen",
"canvasIsTransforming": "Leinwand ist beschäftigt (wird transformiert)",
"canvasIsRasterizing": "Leinwand ist beschäftigt (wird gerastert)",
@@ -750,7 +725,6 @@
"parametersNotSet": "Parameter nicht zurückgerufen",
"addedToBoard": "Dem Board hinzugefügt",
"loadedWithWarnings": "Workflow mit Warnungen geladen",
"imageSaved": "Bild gespeichert",
"linkCopied": "Link kopiert",
"problemCopyingLayer": "Ebene kann nicht kopiert werden",
"problemSavingLayer": "Ebene kann nicht gespeichert werden",
@@ -761,8 +735,6 @@
"prunedQueue": "Warteschlange bereinigt",
"modelAddedSimple": "Modell zur Warteschlange hinzugefügt",
"parametersSet": "Parameter zurückgerufen",
"imageNotLoadedDesc": "Bild konnte nicht gefunden werden",
"setControlImage": "Als Kontrollbild festlegen",
"sentToUpscale": "An Vergrößerung gesendet",
"parameterNotSetDescWithMessage": "{{parameter}} kann nicht zurückgerufen werden: {{message}}",
"unableToLoadImageMetadata": "Bildmetadaten können nicht geladen werden",
@@ -775,7 +747,6 @@
"parameterSet": "Parameter zurückgerufen",
"importFailed": "Import fehlgeschlagen",
"importSuccessful": "Import erfolgreich",
"setNodeField": "Als Knotenfeld festlegen",
"somethingWentWrong": "Etwas ist schief gelaufen",
"workflowLoaded": "Arbeitsablauf geladen",
"workflowDeleted": "Arbeitsablauf gelöscht",
@@ -783,16 +754,12 @@
"layerCopiedToClipboard": "Ebene in die Zwischenablage kopiert",
"sentToCanvas": "An Leinwand gesendet",
"problemDeletingWorkflow": "Problem beim Löschen des Arbeitsablaufs",
"uploadFailedInvalidUploadDesc_withCount_one": "Darf maximal 1 PNG-, JPEG- oder WEBP-Bild sein.",
"uploadFailedInvalidUploadDesc_withCount_other": "Dürfen maximal {{count}} PNG-, JPEG- oder WEBP-Bild sein.",
"problemRetrievingWorkflow": "Problem beim Abrufen des Arbeitsablaufs",
"uploadFailedInvalidUploadDesc": "Müssen PNG-, JPEG- oder WEBP-Bilder sein.",
"pasteSuccess": "Eingefügt in {{destination}}",
"pasteFailed": "Einfügen fehlgeschlagen",
"unableToCopy": "Kopieren nicht möglich",
"unableToCopyDesc_theseSteps": "diese Schritte",
"noRasterLayers": "Keine Rasterebenen gefunden",
"noActiveRasterLayers": "Keine aktiven Rasterebenen",
"noVisibleRasterLayers": "Keine sichtbaren Rasterebenen"
},
"accessibility": {
@@ -845,16 +812,13 @@
"archiveBoard": "Ordner archivieren",
"archived": "Archiviert",
"noBoards": "Kein {{boardType}} Ordner",
"hideBoards": "Ordner verstecken",
"viewBoards": "Ordner ansehen",
"deletedPrivateBoardsCannotbeRestored": "Gelöschte Boards können nicht wiederhergestellt werden. Wenn Sie „Nur Board löschen“ wählen, werden die Bilder in einen privaten, nicht kategorisierten Status für den Ersteller des Bildes versetzt.",
"assetsWithCount_one": "{{count}} in der Sammlung",
"assetsWithCount_other": "{{count}} in der Sammlung",
"deletedBoardsCannotbeRestored": "Gelöschte Ordner können nicht wiederhergestellt werden. Die Auswahl von \"Nur Ordner löschen\" verschiebt Bilder in einen unkategorisierten Zustand.",
"updateBoardError": "Fehler beim Aktualisieren des Ordners",
"uncategorizedImages": "Nicht kategorisierte Bilder",
"deleteAllUncategorizedImages": "Alle nicht kategorisierten Bilder löschen",
"deletedImagesCannotBeRestored": "Gelöschte Bilder können nicht wiederhergestellt werden."
"deleteAllUncategorizedImages": "Alle nicht kategorisierten Bilder löschen"
},
"queue": {
"status": "Status",
@@ -956,8 +920,6 @@
"allPrompts": "Alle Prompts",
"imageDimensions": "Bilder Auslösungen",
"parameterSet": "Parameter {{parameter}} setzen",
"recallParameter": "{{label}} Abrufen",
"parsingFailed": "Parsing Fehlgeschlagen",
"canvasV2Metadata": "Leinwand",
"guidance": "Führung",
"seamlessXAxis": "Nahtlose X Achse",
@@ -1240,9 +1202,7 @@
"collectionFieldType": "{{name}} (Sammlung)",
"connectionWouldCreateCycle": "Verbindung würde einen Kreislauf/cycle schaffen",
"inputMayOnlyHaveOneConnection": "Eingang darf nur eine Verbindung haben",
"hideLegendNodes": "Feldtyp-Legende ausblenden",
"integer": "Ganze Zahl",
"addLinearView": "Zur linearen Ansicht hinzufügen",
"currentImageDescription": "Zeigt das aktuelle Bild im Node-Editor an",
"ipAdapter": "IP-Adapter",
"hideMinimapnodes": "Miniatur-Kartenansicht ausblenden",
@@ -1251,7 +1211,6 @@
"reloadNodeTemplates": "Knoten-Vorlagen neu laden",
"newWorkflow": "Neuer Arbeitsablauf / Workflow",
"newWorkflowDesc": "Einen neuen Arbeitsablauf erstellen?",
"noFieldsLinearview": "Keine Felder zur linearen Ansicht hinzugefügt",
"clearWorkflow": "Workflow löschen",
"clearWorkflowDesc": "Diesen Arbeitsablauf löschen und neu starten?",
"noConnectionInProgress": "Es besteht keine Verbindung",
@@ -1259,7 +1218,6 @@
"nodeVersion": "Knoten Version",
"node": "Knoten",
"nodeSearch": "Knoten suchen",
"removeLinearView": "Entfernen aus Linear View",
"nodeOutputs": "Knoten-Ausgänge",
"nodeTemplate": "Knoten-Vorlage",
"nodeType": "Knotentyp",
@@ -1270,7 +1228,6 @@
"clearWorkflowDesc2": "Ihr aktueller Arbeitsablauf hat ungespeicherte Änderungen.",
"scheduler": "Planer",
"showMinimapnodes": "MiniMap anzeigen",
"showLegendNodes": "Feldtyp-Legende anzeigen",
"executionStateCompleted": "Erledigt",
"downloadWorkflow": "Workflow JSON herunterladen",
"executionStateInProgress": "In Bearbeitung",
@@ -1280,7 +1237,6 @@
"fieldTypesMustMatch": "Feldtypen müssen übereinstimmen",
"fitViewportNodes": "An Ansichtsgröße anpassen",
"loadingNodes": "Lade Nodes...",
"mismatchedVersion": "Ungültiger Knoten: Knoten {{node}} vom Typ {{type}} hat keine passende Version (Update versuchen?)",
"fullyContainNodesHelp": "Nodes müssen vollständig innerhalb der Auswahlbox sein, um ausgewählt werden zu können",
"noWorkflow": "Kein Workflow",
"executionStateError": "Fehler",
@@ -1292,9 +1248,7 @@
"sourceNodeDoesNotExist": "Ungültiger Rand: Quell- / Ausgabe-Knoten {{node}} existiert nicht",
"updateAllNodes": "Update Knoten",
"allNodesUpdated": "Alle Knoten aktualisiert",
"unknownTemplate": "Unbekannte Vorlage",
"updateApp": "Update App",
"unknownInput": "Unbekannte Eingabe: {{name}}",
"unknownNodeType": "Unbekannter Knotentyp",
"float": "Kommazahlen",
"enum": "Aufzählung",
@@ -1310,7 +1264,6 @@
"workflowAuthor": "Autor",
"graph": "Graph",
"workflowDescription": "Kurze Beschreibung",
"versionUnknown": " Version unbekannt",
"workflow": "Arbeitsablauf",
"noGraph": "Kein Graph",
"version": "Version",
@@ -1328,7 +1281,6 @@
"unknownErrorValidatingWorkflow": "Unbekannter Fehler beim Validieren des Arbeitsablaufes",
"inputFieldTypeParseError": "Typ des Eingabefelds {{node}}.{{field}} kann nicht analysiert werden ({{message}})",
"workflowSettings": "Arbeitsablauf Editor Einstellungen",
"unableToLoadWorkflow": "Arbeitsablauf kann nicht geladen werden",
"viewMode": "In linearen Ansicht verwenden",
"unableToValidateWorkflow": "Arbeitsablauf kann nicht validiert werden",
"outputFieldTypeParseError": "Typ des Ausgabefelds {{node}}.{{field}} kann nicht analysiert werden ({{message}})",
@@ -1344,7 +1296,6 @@
"arithmeticSequence": "Arithmetische Folge",
"noBatchGroup": "keine Gruppe",
"generatorNoValues": "leer",
"generatorLoading": "wird geladen",
"generatorLoadFromFile": "Aus Datei laden",
"showEdgeLabels": "Kantenbeschriftungen anzeigen",
"downloadWorkflowError": "Fehler beim Herunterladen des Arbeitsablaufs",
@@ -1352,14 +1303,11 @@
"description": "Beschreibung",
"loadWorkflowDesc": "Arbeitsablauf laden?",
"loadWorkflowDesc2": "Ihr aktueller Arbeitsablauf enthält nicht gespeicherte Änderungen.",
"loadingTemplates": "Lade {{name}}",
"missingSourceOrTargetHandle": "Fehlender Quell- oder Zielgriff",
"missingSourceOrTargetNode": "Fehlender Quell- oder Zielknoten",
"showEdgeLabelsHelp": "Beschriftungen an Kanten anzeigen, um die verknüpften Knoten zu kennzeichnen"
},
"hrf": {
"enableHrf": "Korrektur für hohe Auflösungen",
"upscaleMethod": "Vergrößerungsmethode",
"metadata": {
"strength": "Auflösungs-Fix Stärke",
"enabled": "Auflösungs-Fix aktiviert",
@@ -1370,11 +1318,9 @@
"models": {
"noMatchingModels": "Keine passenden Modelle",
"loading": "lade",
"noMatchingLoRAs": "Keine passenden LoRAs",
"noModelsAvailable": "Keine Modelle verfügbar",
"selectModel": "Wählen ein Modell aus",
"noRefinerModelsInstalled": "Keine SDXL Refiner-Modelle installiert",
"noLoRAsInstalled": "Keine LoRAs installiert",
"addLora": "LoRA hinzufügen",
"defaultVAE": "Standard VAE",
"lora": "LoRA",
@@ -1404,31 +1350,23 @@
"workflows": "Arbeitsabläufe",
"workflowName": "Arbeitsablauf-Name",
"saveWorkflowAs": "Arbeitsablauf speichern als",
"searchWorkflows": "Suche Arbeitsabläufe",
"newWorkflowCreated": "Neuer Arbeitsablauf erstellt",
"problemSavingWorkflow": "Problem beim Speichern des Arbeitsablaufs",
"problemLoading": "Problem beim Laden von Arbeitsabläufen",
"downloadWorkflow": "Speichern als",
"savingWorkflow": "Speichere Arbeitsablauf...",
"saveWorkflow": "Arbeitsablauf speichern",
"noWorkflows": "Keine Arbeitsabläufe",
"workflowLibrary": "Bibliothek",
"unnamedWorkflow": "Unbenannter Arbeitsablauf",
"noDescription": "Keine Beschreibung",
"clearWorkflowSearchFilter": "Suchfilter zurücksetzen",
"workflowEditorMenu": "Arbeitsablauf-Editor Menü",
"deleteWorkflow": "Arbeitsablauf löschen",
"workflowSaved": "Arbeitsablauf gespeichert",
"uploadWorkflow": "Aus Datei laden",
"openWorkflow": "Arbeitsablauf öffnen",
"saveWorkflowToProject": "Arbeitsablauf in Projekt speichern",
"workflowCleared": "Arbeitsablauf gelöscht",
"loading": "Lade Arbeitsabläufe",
"name": "Name",
"ascending": "Aufsteigend",
"defaultWorkflows": "Standard Arbeitsabläufe",
"userWorkflows": "Benutzer Arbeitsabläufe",
"projectWorkflows": "Projekt Arbeitsabläufe",
"opened": "Geöffnet",
"loadWorkflow": "Arbeitsablauf $t(common.load)",
"updated": "Aktualisiert",
@@ -1442,12 +1380,10 @@
"copyShareLink": "Teilen-Link kopieren",
"download": "Herunterladen",
"convertGraph": "Graph konvertieren",
"filterByTags": "Nach Tags filtern",
"yourWorkflows": "Ihre Arbeitsabläufe",
"recentlyOpened": "Kürzlich geöffnet"
},
"sdxl": {
"concatPromptStyle": "Verknüpfen von Prompt & Stil",
"scheduler": "Planer",
"steps": "Schritte"
},
@@ -1459,13 +1395,11 @@
"addPromptTrigger": "Prompt-Trigger hinzufügen",
"compatibleEmbeddings": "Kompatible Einbettungen",
"replace": "Ersetzen",
"insert": "Einfügen",
"discard": "Verwerfen",
"generateFromImage": "Prompt aus Bild generieren",
"expandCurrentPrompt": "Aktuelle Prompt erweitern",
"uploadImageForPromptGeneration": "Bild zur Prompt-Generierung hochladen",
"expandingPrompt": "Prompt wird erweitert...",
"resultTitle": "Prompt-Erweiterung abgeschlossen"
"expandingPrompt": "Prompt wird erweitert..."
},
"ui": {
"tabs": {
@@ -1604,8 +1538,6 @@
"opacity": "Opazität",
"removeBookmark": "Lesezeichen entfernen",
"rasterLayer": "Rasterebene",
"rasterLayers_withCount_visible": "Rasterebenen ({{count}})",
"controlLayers_withCount_visible": "Kontroll-Ebenen ({{count}})",
"deleteSelected": "Ausgewählte löschen",
"newRegionalReferenceImageError": "Problem beim Erstellen eines regionalen Referenzbilds",
"newControlLayerOk": "Kontroll-Ebene erstellt",
@@ -1613,10 +1545,8 @@
"newRasterLayerOk": "Rasterebene erstellt",
"moveToFront": "Nach vorne bringen",
"copyToClipboard": "In die Zwischenablage kopieren",
"controlLayers_withCount_hidden": "Kontroll-Ebenen ({{count}} ausgeblendet)",
"clearCaches": "Cache leeren",
"controlLayer": "Kontroll-Ebene",
"rasterLayers_withCount_hidden": "Rasterebenen ({{count}} ausgeblendet)",
"transparency": "Transparenz",
"canvas": "Leinwand",
"global": "Global",
@@ -1639,9 +1569,7 @@
"weight": "Gewichtung",
"addReferenceImage": "$t(controlLayers.referenceImage) hinzufügen",
"addInpaintMask": "$t(controlLayers.inpaintMask) hinzufügen",
"addGlobalReferenceImage": "$t(controlLayers.globalReferenceImage) hinzufügen",
"regionalGuidance": "Regionale Führung",
"globalReferenceImages_withCount_visible": "Globale Referenzbilder ({{count}})",
"addPositivePrompt": "$t(controlLayers.prompt) hinzufügen",
"locked": "Gesperrt",
"showHUD": "HUD anzeigen",
@@ -1649,16 +1577,12 @@
"addRasterLayer": "$t(controlLayers.rasterLayer) hinzufügen",
"addRegionalGuidance": "$t(controlLayers.regionalGuidance) hinzufügen",
"addControlLayer": "$t(controlLayers.controlLayer) hinzufügen",
"newCanvasSession": "Neue Leinwand-Sitzung",
"replaceLayer": "Ebene ersetzen",
"newGallerySession": "Neue Galerie-Sitzung",
"unlocked": "Entsperrt",
"showProgressOnCanvas": "Fortschritt auf Leinwand anzeigen",
"controlMode": {
"balanced": "Ausgewogen"
},
"globalReferenceImages_withCount_hidden": "Globale Referenzbilder ({{count}} ausgeblendet)",
"sendToGallery": "An Galerie senden",
"stagingArea": {
"accept": "Annehmen",
"next": "Nächste",
@@ -1666,8 +1590,6 @@
"discard": "Verwerfen",
"previous": "Vorherige"
},
"regionalGuidance_withCount_visible": "Regionale Führung ({{count}})",
"regionalGuidance_withCount_hidden": "Regionale Führung ({{count}} ausgeblendet)",
"settings": {
"snapToGrid": {
"on": "Ein",
@@ -1677,8 +1599,6 @@
},
"layer_one": "Ebene",
"layer_other": "Ebenen",
"layer_withCount_one": "Ebene ({{count}})",
"layer_withCount_other": "Ebenen ({{count}})",
"fill": {
"fillStyle": "Füllstil",
"diagonal": "Diagonal",

View File

@@ -43,6 +43,8 @@
"move": "Move",
"movingImagesToBoard_one": "Moving {{count}} image to board:",
"movingImagesToBoard_other": "Moving {{count}} images to board:",
"movingVideosToBoard_one": "Moving {{count}} video to board:",
"movingVideosToBoard_other": "Moving {{count}} videos to board:",
"myBoard": "My Board",
"noBoards": "No {{boardType}} Boards",
"noMatching": "No matching Boards",
@@ -59,6 +61,8 @@
"imagesWithCount_other": "{{count}} images",
"assetsWithCount_one": "{{count}} asset",
"assetsWithCount_other": "{{count}} assets",
"videosWithCount_one": "{{count}} video",
"videosWithCount_other": "{{count}} videos",
"updateBoardError": "Error updating board"
},
"accordions": {
@@ -361,6 +365,9 @@
"deleteImage_one": "Delete Image",
"deleteImage_other": "Delete {{count}} Images",
"deleteImagePermanent": "Deleted images cannot be restored.",
"deleteVideo_one": "Delete Video",
"deleteVideo_other": "Delete {{count}} Videos",
"deleteVideoPermanent": "Deleted videos cannot be restored.",
"displayBoardSearch": "Board Search",
"displaySearch": "Image Search",
"download": "Download",
@@ -380,9 +387,10 @@
"sortDirection": "Sort Direction",
"showStarredImagesFirst": "Show Starred Images First",
"noImageSelected": "No Image Selected",
"noVideoSelected": "No Video Selected",
"noImagesInGallery": "No Images to Display",
"starImage": "Star Image",
"unstarImage": "Unstar Image",
"starImage": "Star",
"unstarImage": "Unstar",
"unableToLoad": "Unable to load Gallery",
"deleteSelection": "Delete Selection",
"downloadSelection": "Download Selection",
@@ -411,7 +419,9 @@
"openViewer": "Open Viewer",
"closeViewer": "Close Viewer",
"move": "Move",
"useForPromptGeneration": "Use for Prompt Generation"
"useForPromptGeneration": "Use for Prompt Generation",
"videos": "Videos",
"videosTab": "Videos you've created and saved within Invoke."
},
"hotkeys": {
"hotkeys": "Hotkeys",
@@ -456,6 +466,10 @@
"title": "Select the Queue Tab",
"desc": "Selects the Queue tab."
},
"selectVideoTab": {
"title": "Select the Video Tab",
"desc": "Selects the Video tab."
},
"focusPrompt": {
"title": "Focus Prompt",
"desc": "Move cursor focus to the positive prompt."
@@ -482,6 +496,9 @@
"key": "1"
}
},
"video": {
"title": "Video"
},
"canvas": {
"title": "Canvas",
"selectBrushTool": {
@@ -572,9 +589,13 @@
"title": "Prev Layer",
"desc": "Select the previous layer in the list."
},
"setFillToWhite": {
"title": "Set Color to White",
"desc": "Set the current tool color to white."
"setFillColorsToDefault": {
"title": "Set Colors to Default",
"desc": "Set the current tool colors to default."
},
"toggleFillColor": {
"title": "Toggle Fill Color",
"desc": "Toggle the current tool fill color."
},
"filterSelected": {
"title": "Filter",
@@ -771,6 +792,9 @@
}
}
},
"lora": {
"weight": "Weight"
},
"metadata": {
"allPrompts": "All Prompts",
"cfgScale": "CFG scale",
@@ -781,11 +805,13 @@
"guidance": "Guidance",
"height": "Height",
"imageDetails": "Image Details",
"videoDetails": "Video Details",
"imageDimensions": "Image Dimensions",
"metadata": "Metadata",
"model": "Model",
"negativePrompt": "Negative Prompt",
"noImageDetails": "No image details found",
"noVideoDetails": "No video details found",
"noMetaData": "No metadata found",
"noRecallParameters": "No parameters to recall found",
"parameterSet": "Parameter {{parameter}} set",
@@ -803,7 +829,11 @@
"vae": "VAE",
"width": "Width",
"workflow": "Workflow",
"canvasV2Metadata": "Canvas Layers"
"canvasV2Metadata": "Canvas Layers",
"videoModel": "Model",
"videoDuration": "Duration",
"videoAspectRatio": "Aspect Ratio",
"videoResolution": "Resolution"
},
"modelManager": {
"active": "active",
@@ -1189,6 +1219,7 @@
},
"parameters": {
"aspect": "Aspect",
"duration": "Duration",
"lockAspectRatio": "Lock Aspect Ratio",
"swapDimensions": "Swap Dimensions",
"setToOptimalSize": "Optimize size for model",
@@ -1213,9 +1244,14 @@
"height": "Height",
"imageFit": "Fit Initial Image To Output Size",
"images": "Images",
"images_withCount_one": "Image",
"images_withCount_other": "Images",
"videos_withCount_one": "Video",
"videos_withCount_other": "Videos",
"infillMethod": "Infill Method",
"infillColorValue": "Fill Color",
"info": "Info",
"startingFrameImage": "Start Frame",
"invoke": {
"addingImagesTo": "Adding images to",
"modelDisabledForTrial": "Generating with {{modelName}} is not available on trial accounts. Visit your account settings to upgrade.",
@@ -1239,6 +1275,7 @@
"batchNodeCollectionSizeMismatchNoGroupId": "Batch group collection size mismatch",
"batchNodeCollectionSizeMismatch": "Collection size mismatch on Batch {{batchGroupId}}",
"noModelSelected": "No model selected",
"noStartingFrameImage": "No starting frame image",
"noT5EncoderModelSelected": "No T5 Encoder model selected for FLUX generation",
"noFLUXVAEModelSelected": "No VAE model selected for FLUX generation",
"noCLIPEmbedModelSelected": "No CLIP Embed model selected for FLUX generation",
@@ -1251,7 +1288,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",
"incompatibleLoRAs": "Incompatible LoRA(s) added",
"canvasIsFiltering": "Canvas is busy (filtering)",
"canvasIsTransforming": "Canvas is busy (transforming)",
"canvasIsRasterizing": "Canvas is busy (rasterizing)",
@@ -1261,7 +1298,8 @@
"noNodesInGraph": "No nodes in graph",
"systemDisconnected": "System disconnected",
"promptExpansionPending": "Prompt expansion in progress",
"promptExpansionResultPending": "Please accept or discard your prompt expansion result"
"promptExpansionResultPending": "Please accept or discard your prompt expansion result",
"videoIsDisabled": "Video generation is not enabled for {{accountType}} accounts."
},
"maskBlur": "Mask Blur",
"negativePromptPlaceholder": "Negative Prompt",
@@ -1279,9 +1317,11 @@
"seamlessXAxis": "Seamless X Axis",
"seamlessYAxis": "Seamless Y Axis",
"seed": "Seed",
"videoActions": "Video Actions",
"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",
@@ -1293,6 +1333,7 @@
"postProcessing": "Post-Processing (Shift + U)",
"processImage": "Process Image",
"upscaling": "Upscaling",
"video": "Video",
"useAll": "Use All",
"useSize": "Use Size",
"useCpuNoise": "Use CPU Noise",
@@ -1304,6 +1345,7 @@
"gaussianBlur": "Gaussian Blur",
"boxBlur": "Box Blur",
"staged": "Staged",
"resolution": "Resolution",
"modelDisabledForTrial": "Generating with {{modelName}} is not available on trial accounts. Visit your <LinkComponent>account settings</LinkComponent> to upgrade."
},
"dynamicPrompts": {
@@ -2233,6 +2275,8 @@
},
"fill": {
"fillColor": "Fill Color",
"bgFillColor": "Background Color",
"fgFillColor": "Foreground Color",
"fillStyle": "Fill Style",
"solid": "Solid",
"grid": "Grid",
@@ -2565,19 +2609,30 @@
"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"
"imageViewer": "Viewer",
"canvas": "Canvas",
"video": "Video"
},
"launchpad": {
"workflowsTitle": "Go deep with Workflows.",
"upscalingTitle": "Upscale and add detail.",
"canvasTitle": "Edit and refine on Canvas.",
"generateTitle": "Generate images from text prompts.",
"videoTitle": "Generate videos from text prompts.",
"video": {
"startingFrameCalloutTitle": "Add a Starting Frame",
"startingFrameCalloutDesc": "Add an image to control the first frame of your video."
},
"addStartingFrame": {
"title": "Add a Starting Frame",
"description": "Add an image to control the first frame of your video."
},
"modelGuideText": "Want to learn what prompts work best for each model?",
"modelGuideLink": "Check out our Model Guide.",
"createNewWorkflowFromScratch": "Create a new Workflow from scratch",
@@ -2652,6 +2707,10 @@
}
}
},
"video": {
"noVideoSelected": "No video selected",
"selectFromGallery": "Select a video from the gallery to play"
},
"system": {
"enableLogging": "Enable Logging",
"logLevel": {
@@ -2689,8 +2748,8 @@
"whatsNew": {
"whatsNewInInvoke": "What's New in Invoke",
"items": [
"Canvas: Color Picker does not sample alpha, bbox respects aspect ratio lock when resizing shuffle button for number fields in Workflow Builder, hide pixel dimension sliders when using a model that doesn't support them",
"Workflows: Add a Shuffle button to number input fields"
"Canvas: Separate foreground and background colors - toggle with 'x', reset to black and white with 'd'",
"LoRAs: Set default weights for individual LoRAs in the Model Manager tab"
],
"readReleaseNotes": "Read Release Notes",
"watchRecentReleaseVideos": "Watch Recent Release Videos",

View File

@@ -47,11 +47,8 @@
"editor": "Editor",
"orderBy": "Ordenar por",
"file": "Archivo",
"goTo": "Ir a",
"imageFailedToLoad": "No se puede cargar la imagen",
"saveAs": "Guardar Como",
"somethingWentWrong": "Algo salió mal",
"nextPage": "Página Siguiente",
"selected": "Seleccionado",
"tab": "Tabulador",
"positivePrompt": "Prompt Positivo",
@@ -61,7 +58,6 @@
"unknown": "Desconocido",
"input": "Entrada",
"template": "Plantilla",
"prevPage": "Página Anterior",
"red": "Rojo",
"alpha": "Transparencia",
"outputs": "Resultados",
@@ -94,8 +90,6 @@
"edit": "Editar",
"safetensors": "Safetensors",
"toResolve": "Para resolver",
"localSystem": "Sistema local",
"notInstalled": "No $t(common.installed)",
"outpaint": "outpaint",
"simple": "Sencillo",
"close": "Cerrar"
@@ -104,7 +98,6 @@
"galleryImageSize": "Tamaño de la imagen",
"gallerySettings": "Ajustes de la galería",
"autoSwitchNewImages": "Auto seleccionar Imágenes nuevas",
"noImagesInGallery": "No hay imágenes para mostrar",
"deleteImage_one": "Eliminar Imagen",
"deleteImage_many": "Eliminar {{count}} Imágenes",
"deleteImage_other": "Eliminar {{count}} Imágenes",
@@ -118,9 +111,7 @@
"selectForCompare": "Seleccionar para comparar",
"alwaysShowImageSizeBadge": "Mostrar siempre las dimensiones de la imagen",
"currentlyInUse": "Esta imagen se utiliza actualmente con las siguientes funciones:",
"unableToLoad": "No se puede cargar la galería",
"selectAllOnPage": "Seleccionar todo en la página",
"selectAnImageToCompare": "Seleccione una imagen para comparar",
"bulkDownloadFailed": "Error en la descarga",
"compareHelp2": "Presione <Kbd> M </Kbd> para recorrer los modos de comparación.",
"move": "Mover",
@@ -145,7 +136,6 @@
"exitBoardSearch": "Finalizar búsqueda",
"exitSearch": "Salir de la búsqueda de imágenes",
"featuresWillReset": "Si elimina esta imagen, dichas funciones se restablecerán inmediatamente.",
"jump": "Omitir",
"loading": "Cargando",
"newestFirst": "La más nueva primero",
"unstarImage": "Dejar de ser favorita",
@@ -163,9 +153,7 @@
"boardsSettings": "Ajustes de los tableros",
"imagesSettings": "Configuración de imágenes de la galería",
"compareHelp3": "Presione <Kbd> C </Kbd> para intercambiar las imágenes comparadas.",
"showArchivedBoards": "Mostrar paneles archivados",
"closeViewer": "Cerrar visor",
"openViewer": "Abrir visor"
"showArchivedBoards": "Mostrar paneles archivados"
},
"modelManager": {
"modelManager": "Gestor de Modelos",
@@ -239,12 +227,10 @@
"scaledHeight": "Alto escalado",
"infillMethod": "Método de relleno",
"tileSize": "Tamaño del mosaico",
"downloadImage": "Descargar imagen",
"usePrompt": "Usar Entrada",
"useSeed": "Usar Semilla",
"useAll": "Usar Todo",
"info": "Información",
"showOptionsPanel": "Mostrar panel lateral (O o T)",
"symmetry": "Simetría",
"copyImage": "Copiar la imagen",
"general": "General",
@@ -323,8 +309,6 @@
"hideMinimapnodes": "Ocultar el minimapa",
"fitViewportNodes": "Ajustar la vista",
"zoomOutNodes": "Alejar",
"hideLegendNodes": "Ocultar la leyenda del tipo de campo",
"showLegendNodes": "Mostrar la leyenda del tipo de campo",
"showMinimapnodes": "Mostrar el minimapa",
"reloadNodeTemplates": "Recargar las plantillas de nodos",
"loadWorkflow": "Cargar el flujo de trabajo",
@@ -361,7 +345,6 @@
"assetsWithCount_one": "{{count}} activo",
"assetsWithCount_many": "{{count}} activos",
"assetsWithCount_other": "{{count}} activos",
"hideBoards": "Ocultar paneles",
"addPrivateBoard": "Agregar un panel privado",
"addSharedBoard": "Añadir panel compartido",
"boards": "Paneles",
@@ -372,7 +355,6 @@
"noBoards": "No hay paneles {{boardType}}",
"shared": "Paneles compartidos",
"deletedPrivateBoardsCannotbeRestored": "Los paneles eliminados no se pueden restaurar. Al elegir \"Eliminar solo el panel\", las imágenes se colocan en un estado privado y sin categoría para el creador de la imagen.",
"viewBoards": "Ver paneles",
"private": "Paneles privados",
"updateBoardError": "No se pudo actualizar el panel"
},
@@ -494,15 +476,11 @@
"layer_one": "Capa",
"layer_many": "Capas",
"layer_other": "Capas",
"layer_withCount_one": "({{count}}) capa",
"layer_withCount_many": "({{count}}) capas",
"layer_withCount_other": "({{count}}) capas",
"copyToClipboard": "Copiar al portapapeles"
},
"whatsNew": {
"readReleaseNotes": "Leer las notas de la versión",
"watchRecentReleaseVideos": "Ver videos de versiones recientes",
"watchUiUpdatesOverview": "Descripción general de las actualizaciones de la interfaz de usuario de Watch",
"whatsNewInInvoke": "Novedades en Invoke",
"items": [
"<StrongComponent>SD 3.5</StrongComponent>: compatibilidad con SD 3.5 Medium y Large."
@@ -527,13 +505,11 @@
},
"hrf": {
"hrf": "Solución de alta resolución",
"enableHrf": "Activar corrección de alta resolución",
"metadata": {
"enabled": "Corrección de alta resolución activada",
"strength": "Forzar la corrección de alta resolución",
"method": "Método de corrección de alta resolución"
},
"upscaleMethod": "Método de expansión"
}
},
"prompt": {
"addPromptTrigger": "Añadir activador de los avisos",
@@ -591,10 +567,6 @@
"title": "Ajustar capas al lienzo",
"desc": "Escala y posiciona la vista para que se ajuste a todas las capas visibles."
},
"setFillToWhite": {
"title": "Establecer color en blanco",
"desc": "Establece el color actual de la herramienta en blanco."
},
"resetSelected": {
"title": "Restablecer capa",
"desc": "Restablecer la capa seleccionada. Solo se aplica a Máscara de retoque y Guía regional."
@@ -868,10 +840,8 @@
"seed": "Semilla",
"strength": "Forzar imagen a imagen",
"recallParameters": "Parámetros de recuperación",
"recallParameter": "Recuperar {{label}}",
"steps": "Pasos",
"noRecallParameters": "Sin parámetros para recuperar",
"parsingFailed": "Error al analizar"
"noRecallParameters": "Sin parámetros para recuperar"
},
"system": {
"logLevel": {

View File

@@ -28,7 +28,6 @@
"gallery": {
"galleryImageSize": "Kuvan koko",
"gallerySettings": "Gallerian asetukset",
"autoSwitchNewImages": "Vaihda uusiin kuviin automaattisesti",
"noImagesInGallery": "Ei kuvia galleriassa"
"autoSwitchNewImages": "Vaihda uusiin kuviin automaattisesti"
}
}

View File

@@ -27,21 +27,15 @@
"error": "Erreur",
"installed": "Installé",
"format": "format",
"goTo": "Aller à",
"input": "Entrée",
"linear": "Linéaire",
"localSystem": "Système local",
"learnMore": "En savoir plus",
"modelManager": "Gestionnaire de modèle",
"notInstalled": "Non $t(common.installed)",
"openInNewTab": "Ouvrir dans un nouvel onglet",
"somethingWentWrong": "Une erreur s'est produite",
"created": "Créé",
"tab": "Onglet",
"folder": "Dossier",
"imageFailedToLoad": "Impossible de charger l'Image",
"prevPage": "Page précédente",
"nextPage": "Page suivante",
"selected": "Sélectionné",
"save": "Enregistrer",
"updated": "Mis à jour",
@@ -111,7 +105,6 @@
"min": "Min",
"max": "Max",
"values": "Valeurs",
"resetToDefaults": "Réinitialiser par défaut",
"seed": "Graine",
"combinatorial": "Combinatoire"
},
@@ -119,11 +112,9 @@
"galleryImageSize": "Taille de l'image",
"gallerySettings": "Paramètres de la galerie",
"autoSwitchNewImages": "Basculer automatiquement vers de nouvelles images",
"noImagesInGallery": "Aucune image à afficher",
"bulkDownloadRequestedDesc": "Votre demande de téléchargement est en cours de traitement. Cela peut prendre quelques instants.",
"deleteSelection": "Supprimer la sélection",
"selectAllOnPage": "Séléctionner toute la page",
"unableToLoad": "Impossible de charger la Galerie",
"featuresWillReset": "Si vous supprimez cette image, ces fonctionnalités vont être réinitialisés.",
"loading": "Chargement",
"sortDirection": "Direction de tri",
@@ -149,7 +140,6 @@
"openInViewer": "Ouvrir dans le Visualiseur",
"showArchivedBoards": "Montrer les Planches archivées",
"selectForCompare": "Séléctionner pour comparaison",
"selectAnImageToCompare": "Séléctionner une Image à comparer",
"exitCompare": "Sortir de la comparaison",
"compareHelp2": "Appuyez sur <Kbd>M</Kbd> pour faire défiler les modes de comparaison.",
"swapImages": "Échanger les Images",
@@ -157,10 +147,7 @@
"compareHelp1": "Maintenir <Kbd>Alt</Kbd> lors du clic d'une image dans la galerie ou en utilisant les flèches du clavier pour changer l'Image à comparer.",
"compareHelp3": "Appuyer sur <Kbd>C</Kbd> pour échanger les images à comparer.",
"image": "image",
"openViewer": "Ouvrir le Visualisateur",
"closeViewer": "Fermer le Visualisateur",
"currentlyInUse": "Cette image est actuellement utilisée dans ces fonctionalités :",
"jump": "Sauter",
"starImage": "Marquer l'Image",
"download": "Téléchargement",
"deleteImage_one": "Supprimer l'Image",
@@ -247,7 +234,6 @@
"metadata": "Métadonnées",
"scanFolder": "Scanner le dossier",
"inplaceInstallDesc": "Installez les modèles sans copier les fichiers. Lors de l'utilisation du modèle, il sera chargé depuis cet emplacement. Si cette option est désactivée, le(s) fichier(s) du modèle seront copiés dans le répertoire des modèles géré par Invoke lors de l'installation.",
"ipAdapters": "Adaptateurs IP",
"installQueue": "File d'attente d'installation",
"modelImageDeleteFailed": "Échec de la suppression de l'image du modèle",
"modelName": "Nom du modèle",
@@ -288,7 +274,6 @@
"scanFolderHelper": "Le dossier sera analysé de manière récursive à la recherche de modèles. Cela peut prendre quelques instants pour des dossiers très volumineux.",
"clipEmbed": "Intégration CLIP",
"spandrelImageToImage": "Image vers Image (Spandrel)",
"starterModelsInModelManager": "Les modèles de démarrage peuvent être trouvés dans le gestionnaire de modèles",
"t5Encoder": "Encodeur T5",
"learnMoreAboutSupportedModels": "En savoir plus sur les modèles que nous prenons en charge",
"includesNModels": "Contient {{n}} modèles et leurs dépendances",
@@ -346,12 +331,10 @@
"infillMethod": "Méthode de Remplissage",
"tileSize": "Taille des Tuiles",
"copyImage": "Copier Image",
"downloadImage": "Télécharger Image",
"usePrompt": "Utiliser la suggestion",
"useSeed": "Utiliser la graine",
"useAll": "Tout utiliser",
"info": "Info",
"showOptionsPanel": "Afficher le panneau latéral (O ou T)",
"invoke": {
"noPrompts": "Aucun prompts généré",
"missingInputForField": "entrée manquante",
@@ -362,21 +345,16 @@
"noModelSelected": "Aucun modèle sélectionné",
"noNodesInGraph": "Aucun nœud dans le graphique",
"systemDisconnected": "Système déconnecté",
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), la hauteur de la bounding box est {{height}}",
"fluxModelIncompatibleScaledBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), la hauteur de la bounding box est {{height}}",
"noFLUXVAEModelSelected": "Aucun modèle VAE sélectionné pour la génération FLUX",
"canvasIsTransforming": "La Toile est occupée (en transformation)",
"canvasIsRasterizing": "La Toile est occupée (en rastérisation)",
"noCLIPEmbedModelSelected": "Aucun modèle CLIP Embed sélectionné pour la génération FLUX",
"canvasIsFiltering": "La Toile est occupée (en filtration)",
"fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), la largeur de la bounding box est {{width}}",
"noT5EncoderModelSelected": "Aucun modèle T5 Encoder sélectionné pour la génération FLUX",
"fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), la largeur de la bounding box mise à l'échelle est {{width}}",
"canvasIsCompositing": "La Toile est occupée (en composition)",
"collectionTooFewItems": "trop peu d'éléments, minimum {{minItems}}",
"collectionTooManyItems": "trop d'éléments, maximum {{maxItems}}",
"canvasIsSelectingObject": "La toile est occupée (sélection d'objet)",
"emptyBatches": "lots vides",
"batchNodeNotConnected": "Noeud de lots non connecté : {{label}}",
"fluxModelMultipleControlLoRAs": "Vous ne pouvez utiliser qu'un seul Control LoRA à la fois",
"collectionNumberLTMin": "{{value}} < {{minimum}} (incl. min)",
@@ -468,9 +446,7 @@
"informationalPopoversDisabled": "Pop-ups d'information désactivés",
"informationalPopoversDisabledDesc": "Les pop-ups d'information ont été désactivés. Activez-les dans les paramètres.",
"confirmOnNewSession": "Confirmer lors d'une nouvelle session",
"modelDescriptionsDisabledDesc": "Les descriptions des modèles dans les menus déroulants ont été désactivées. Activez-les dans les paramètres.",
"enableModelDescriptions": "Activer les descriptions de modèle dans les menus déroulants",
"modelDescriptionsDisabled": "Descriptions de modèle dans les menus déroulants désactivés",
"showDetailedInvocationProgress": "Afficher les détails de progression"
},
"toast": {
@@ -486,17 +462,14 @@
"addedToBoard": "Ajouté aux ressources de la planche {{name}}",
"workflowLoaded": "Workflow chargé",
"connected": "Connecté au serveur",
"setNodeField": "Définir comme champ de nœud",
"imageUploadFailed": "Échec de l'importation de l'image",
"loadedWithWarnings": "Workflow chargé avec des avertissements",
"imageUploaded": "Image importée",
"modelAddedSimple": "Modèle ajouté à la file d'attente",
"setControlImage": "Définir comme image de contrôle",
"workflowDeleted": "Workflow supprimé",
"baseModelChangedCleared_one": "Effacé ou désactivé {{count}} sous-modèle incompatible",
"baseModelChangedCleared_many": "Effacé ou désactivé {{count}} sous-modèles incompatibles",
"baseModelChangedCleared_other": "Effacé ou désactivé {{count}} sous-modèles incompatibles",
"invalidUpload": "Importation invalide",
"problemDownloadingImage": "Impossible de télécharger l'image",
"problemRetrievingWorkflow": "Problème de récupération du Workflow",
"problemDeletingWorkflow": "Problème de suppression du Workflow",
@@ -510,12 +483,10 @@
"errorCopied": "Erreur copiée",
"parametersSet": "Paramètres rappelés",
"somethingWentWrong": "Quelque chose a échoué",
"imageSaved": "Image enregistrée",
"unableToLoadStylePreset": "Impossible de charger le préréglage de style",
"stylePresetLoaded": "Préréglage de style chargé",
"parameterNotSetDescWithMessage": "Impossible de rappeler {{parameter}} : {{message}}",
"importFailed": "Importation échouée",
"imageSavingFailed": "Échec de l'enregistrement de l'image",
"importSuccessful": "Importation réussie",
"outOfMemoryError": "Erreur de mémoire insuffisante",
"sessionRef": "Session : {{sessionId}}",
@@ -523,16 +494,11 @@
"parameterSetDesc": "Rappelé {{parameter}}",
"parameterNotSetDesc": "Impossible de rappeler {{parameter}}",
"layerCopiedToClipboard": "Calque copié dans le presse-papiers",
"layerSavedToAssets": "Calque enregistré dans les ressources",
"problemCopyingLayer": "Impossible de copier la couche",
"baseModelChanged": "Modèle de base changé",
"problemSavingLayer": "Impossible d'enregistrer la couche",
"imageNotLoadedDesc": "Image introuvable",
"linkCopied": "Lien copié",
"imagesWillBeAddedTo": "Les images Importées seront ajoutées au ressources de la Planche {{boardName}}.",
"uploadFailedInvalidUploadDesc_withCount_one": "Doit être au maximum une image PNG ou JPEG.",
"uploadFailedInvalidUploadDesc_withCount_many": "Doit être au maximum {{count}} images PNG ou JPEG.",
"uploadFailedInvalidUploadDesc_withCount_other": "Doit être au maximum {{count}} images PNG ou JPEG.",
"addedToUncategorized": "Ajouté aux ressources de la planche $t(boards.uncategorized)",
"pasteSuccess": "Collé à {{destination}}",
"pasteFailed": "Échec du collage",
@@ -580,8 +546,6 @@
"movingImagesToBoard_one": "Déplacer {{count}} image à cette planche :",
"movingImagesToBoard_many": "Déplacer {{count}} images à cette planche :",
"movingImagesToBoard_other": "Déplacer {{count}} image à cette planche :",
"viewBoards": "Voir les Planches",
"hideBoards": "Cacher les Planches",
"noBoards": "Pas de Planches {{boardType}}",
"shared": "Planches Partagées",
"searchBoard": "Chercher les Planches...",
@@ -712,13 +676,11 @@
"compatibleEmbeddings": "Embeddings Compatibles"
},
"hrf": {
"upscaleMethod": "Méthode d'Agrandissement",
"metadata": {
"enabled": "Correction Haute Résolution Activée",
"strength": "Force de la Correction Haute Résolution",
"method": "Méthode de la Correction Haute Résolution"
},
"enableHrf": "Activer la Correction Haute Résolution",
"hrf": "Correction Haute Résolution"
},
"invocationCache": {
@@ -901,10 +863,6 @@
"desc": "Définit le zoom de la toile à 400 %.",
"title": "Zoomer à 400 %"
},
"setFillToWhite": {
"title": "Définir la couleur sur blanc",
"desc": "Définir la couleur de l'outil actuel sur blanc."
},
"transformSelected": {
"title": "Transformer",
"desc": "Transforme la couche sélectionnée."
@@ -1490,8 +1448,7 @@
"showDynamicPrompts": "Afficher les Prompts dynamiques",
"dynamicPrompts": "Prompts Dynamiques",
"promptsPreview": "Prévisualisation des Prompts",
"loading": "Génération des Pompts Dynamiques...",
"promptsToGenerate": "Prompts à générer"
"loading": "Génération des Pompts Dynamiques..."
},
"metadata": {
"positivePrompt": "Prompt Positif",
@@ -1519,18 +1476,12 @@
"recallParameters": "Rappeler les paramètres",
"imageDimensions": "Dimensions de l'image",
"parameterSet": "Paramètre {{parameter}} défini",
"parsingFailed": "L'analyse a échoué",
"recallParameter": "Rappeler {{label}}",
"canvasV2Metadata": "Toile",
"guidance": "Guide",
"seamlessXAxis": "Axe X sans bords",
"seamlessYAxis": "Axe Y sans bords"
},
"sdxl": {
"freePromptStyle": "Écriture de Prompt manuelle",
"concatPromptStyle": "Lier Prompt & Style",
"negStylePrompt": "Style Prompt Négatif",
"posStylePrompt": "Style Prompt Positif",
"refinerStart": "Démarrer le Refiner",
"denoisingStrength": "Force de débruitage",
"steps": "Étapes",
@@ -1547,8 +1498,6 @@
"nodes": {
"showMinimapnodes": "Afficher la MiniCarte",
"fitViewportNodes": "Ajuster la Vue",
"hideLegendNodes": "Masquer la légende du type de champ",
"showLegendNodes": "Afficher la légende du type de champ",
"hideMinimapnodes": "Masquer MiniCarte",
"zoomOutNodes": "Dézoomer",
"zoomInNodes": "Zoomer",
@@ -1572,9 +1521,7 @@
"colorCodeEdges": "Code de couleur des connexions",
"colorCodeEdgesHelp": "Code couleur des connexions en fonction de leurs champs connectés",
"currentImage": "Image actuelle",
"noFieldsLinearview": "Aucun champ ajouté à la vue linéaire",
"float": "Flottant",
"mismatchedVersion": "Nœud invalide : le nœud {{node}} de type {{type}} a une version incompatible (essayez de mettre à jour?)",
"missingTemplate": "Nœud invalide : le nœud {{node}} de type {{type}} modèle manquant (non installé?)",
"noWorkflow": "Pas de Workflow",
"validateConnectionsHelp": "Prévenir la création de connexions invalides et l'invocation de graphes invalides",
@@ -1585,12 +1532,10 @@
"scheduler": "Planificateur",
"notes": "Notes",
"notesDescription": "Ajouter des notes sur votre workflow",
"unableToLoadWorkflow": "Impossible de charger le Workflow",
"addNode": "Ajouter un nœud",
"problemSettingTitle": "Problème lors de définition du Titre",
"connectionWouldCreateCycle": "La connexion créerait un cycle",
"currentImageDescription": "Affiche l'image actuelle dans l'éditeur de nœuds",
"versionUnknown": " Version inconnue",
"cannotConnectInputToInput": "Impossible de connecter l'entrée à l'entrée",
"addNodeToolTip": "Ajouter un nœud (Shift+A, Espace)",
"fullyContainNodesHelp": "Les nœuds doivent être entièrement à l'intérieur de la zone de sélection pour être sélectionnés",
@@ -1606,7 +1551,6 @@
"nodeSearch": "Rechercher des nœuds",
"collection": "Collection",
"noOutputRecorded": "Aucun résultat enregistré",
"removeLinearView": "Retirer de la vue linéaire",
"snapToGrid": "Aligner sur la grille",
"workflow": "Workflow",
"updateApp": "Mettre à jour l'application",
@@ -1615,7 +1559,6 @@
"noConnectionInProgress": "Aucune connexion en cours",
"nodeType": "Type de nœud",
"workflowContact": "Contact",
"unknownTemplate": "Modèle inconnu",
"unknownNode": "Nœud inconnu",
"workflowVersion": "Version",
"string": "Chaîne de caractères",
@@ -1629,7 +1572,6 @@
"cannotDuplicateConnection": "Impossible de créer des connexions en double",
"resetToDefaultValue": "Réinitialiser à la valeur par défaut",
"unknownNodeType": "Type de nœud inconnu",
"unknownInput": "Entrée inconnue : {{name}}",
"prototypeDesc": "Cette invocation est un prototype. Elle peut subir des modifications majeures lors des mises à jour de l'application et peut être supprimée à tout moment.",
"nodePack": "Paquet de nœuds",
"sourceNodeDoesNotExist": "Connexion invalide : le nœud source/de sortie {{node}} n'existe pas",
@@ -1644,7 +1586,6 @@
"clearWorkflow": "Effacer le Workflow",
"clearWorkflowDesc": "Effacer ce workflow et en commencer un nouveau?",
"unsupportedArrayItemType": "type d'élément de tableau non pris en charge \"{{type}}\"",
"addLinearView": "Ajouter à la vue linéaire",
"collectionOrScalarFieldType": "{{name}} (Unique ou Collection)",
"unableToExtractEnumOptions": "impossible d'extraire les options d'énumération",
"unsupportedAnyOfLength": "trop de membres dans l'union ({{count}})",
@@ -1652,7 +1593,6 @@
"viewMode": "Utiliser en vue linéaire",
"collectionFieldType": "{{name}} (Collection)",
"newWorkflow": "Nouveau Workflow",
"reorderLinearView": "Réorganiser la vue linéaire",
"outputFieldTypeParseError": "Impossible d'analyser le type du champ de sortie {{node}}.{{field}} ({{message}})",
"unsupportedMismatchedUnion": "type CollectionOrScalar non concordant avec les types de base {{firstType}} et {{secondType}}",
"unableToParseFieldType": "impossible d'analyser le type de champ",
@@ -1686,13 +1626,9 @@
"arithmeticSequence": "Séquence Arithmétique",
"uniformRandomDistribution": "Distribution Aléatoire Uniforme",
"noBatchGroup": "aucun groupe",
"generatorLoading": "chargement",
"generatorLoadFromFile": "Charger depuis un Fichier",
"dynamicPromptsRandom": "Prompts Dynamiques (Aléatoire)",
"integerRangeGenerator": "Générateur d'interval d'entiers",
"generateValues": "Générer Valeurs",
"linearDistribution": "Distribution Linéaire",
"floatRangeGenerator": "Générateur d'interval de nombres décimaux",
"generatorNRandomValues_one": "{{count}} valeur aléatoire",
"generatorNRandomValues_many": "{{count}} valeurs aléatoires",
"generatorNRandomValues_other": "{{count}} valeurs aléatoires",
@@ -1712,7 +1648,6 @@
"generatorImagesCategory": "Catégorie",
"generatorImagesFromBoard": "Images de la Planche",
"missingSourceOrTargetHandle": "Manque de gestionnaire source ou cible",
"loadingTemplates": "Chargement de {{name}}",
"loadWorkflowDesc2": "Votre workflow actuel contient des modifications non enregistrées.",
"generatorImages_one": "{{count}} image",
"generatorImages_many": "{{count}} images",
@@ -1723,10 +1658,8 @@
"noModelsAvailable": "Aucun modèle disponible",
"loading": "chargement",
"selectModel": "Sélectionner un modèle",
"noMatchingLoRAs": "Aucun LoRA correspondant",
"lora": "LoRA",
"noRefinerModelsInstalled": "Aucun modèle SDXL Refiner installé",
"noLoRAsInstalled": "Aucun LoRA installé",
"addLora": "Ajouter LoRA",
"defaultVAE": "VAE par défaut",
"concepts": "Concepts"
@@ -1734,11 +1667,8 @@
"workflows": {
"workflowLibrary": "Bibliothèque",
"loading": "Chargement des Workflows",
"searchWorkflows": "Chercher des Workflows",
"workflowCleared": "Workflow effacé",
"noDescription": "Aucune description",
"deleteWorkflow": "Supprimer le Workflow",
"openWorkflow": "Ouvrir le Workflow",
"uploadWorkflow": "Charger à partir d'un fichier",
"workflowName": "Nom du Workflow",
"unnamedWorkflow": "Workflow sans nom",
@@ -1751,8 +1681,6 @@
"problemSavingWorkflow": "Problème de sauvegarde du Workflow",
"workflowEditorMenu": "Menu de l'Éditeur de Workflow",
"newWorkflowCreated": "Nouveau Workflow créé",
"clearWorkflowSearchFilter": "Réinitialiser le filtre de recherche de Workflow",
"problemLoading": "Problème de chargement des Workflows",
"workflowSaved": "Workflow enregistré",
"noWorkflows": "Pas de Workflows",
"ascending": "Ascendant",
@@ -1765,9 +1693,6 @@
"opened": "Ouvert",
"name": "Nom",
"autoLayout": "Mise en page automatique",
"defaultWorkflows": "Workflows par défaut",
"userWorkflows": "Workflows de l'utilisateur",
"projectWorkflows": "Workflows du projet",
"copyShareLink": "Copier le lien de partage",
"chooseWorkflowFromLibrary": "Choisir le Workflow dans la Bibliothèque",
"edit": "Modifer",
@@ -1784,7 +1709,6 @@
"multiLine": "Multi Ligne",
"headingPlaceholder": "En-tête vide",
"emptyRootPlaceholderEditMode": "Faites glisser un élément de formulaire ou un champ de nœud ici pour commencer.",
"emptyRootPlaceholderViewMode": "Cliquez sur Modifier pour commencer à créer un formulaire pour ce workflow.",
"containerPlaceholder": "Conteneur Vide",
"row": "Ligne",
"column": "Colonne",
@@ -1798,10 +1722,8 @@
"builder": "Constructeur de Formulaire",
"resetAllNodeFields": "Réinitialiser tous les champs de nœud",
"deleteAllElements": "Supprimer tous les éléments de formulaire",
"workflowBuilderAlphaWarning": "Le constructeur de workflow est actuellement en version alpha. Il peut y avoir des changements majeurs avant la version stable.",
"showDescription": "Afficher la description"
},
"openLibrary": "Ouvrir la Bibliothèque"
}
},
"whatsNew": {
"whatsNewInInvoke": "Quoi de neuf dans Invoke",
@@ -1810,8 +1732,7 @@
"<StrongComponent>FLUX Guidage Régional (bêta)</StrongComponent> : Notre version bêta de FLUX Guidage Régional est en ligne pour le contrôle des prompt régionaux.",
"Autres améliorations : mise en file d'attente par lots plus rapide, meilleur redimensionnement, sélecteur de couleurs amélioré et nœuds de métadonnées."
],
"readReleaseNotes": "Notes de version",
"watchUiUpdatesOverview": "Aperçu des mises à jour de l'interface utilisateur"
"readReleaseNotes": "Notes de version"
},
"ui": {
"tabs": {
@@ -1828,7 +1749,6 @@
},
"controlLayers": {
"newLayerFromImage": "Nouvelle couche à partir de l'image",
"sendToGalleryDesc": "Appuyer sur Invoker génère et enregistre une image unique dans votre galerie.",
"sendToCanvas": "Envoyer vers la Toile",
"globalReferenceImage": "Image de référence globale",
"newCanvasFromImage": "Nouvelle Toile à partir de l'image",
@@ -1984,7 +1904,6 @@
},
"bookmark": "Marque-page pour Changement Rapide",
"saveLayerToAssets": "Enregistrer la couche dans les ressources",
"stagingOnCanvas": "Mise en attente des images sur",
"enableTransparencyEffect": "Activer l'effet de transparence",
"hidingType": "Masquer {{type}}",
"settings": {
@@ -2015,11 +1934,6 @@
"disableAutoNegative": "Désactiver l'Auto Négatif",
"addNegativePrompt": "Ajouter $t(controlLayers.negativePrompt)",
"addRegionalGuidance": "Ajouter $t(controlLayers.regionalGuidance)",
"controlLayers_withCount_hidden": "Control Layers ({{count}} cachées)",
"rasterLayers_withCount_hidden": "Couche de Rastérisation ({{count}} cachées)",
"regionalGuidance_withCount_hidden": "Guidage Régional ({{count}} caché)",
"rasterLayers_withCount_visible": "Couche de Rastérisation ({{count}})",
"inpaintMasks_withCount_visible": "Masques de remplissage ({{count}})",
"layer_one": "Couche",
"layer_many": "Couches",
"layer_other": "Couches",
@@ -2069,8 +1983,6 @@
"next": "Suivant",
"saveToGallery": "Enregistrer dans la galerie"
},
"viewProgressOnCanvas": "Voir les progrès et les sorties de la scène sur la <Btn>Toile</Btn>.",
"sendToCanvasDesc": "Appuyer sur Invoker met en attente votre travail en cours sur la toile.",
"mergeVisibleError": "Erreur lors de la fusion des calques visibles",
"mergeVisibleOk": "Couches visibles fusionnées",
"clearHistory": "Effacer l'historique",
@@ -2079,8 +1991,6 @@
"duplicate": "Dupliquer",
"enableAutoNegative": "Activer l'Auto Négatif",
"showHUD": "Afficher HUD",
"sendToGallery": "Envoyer à la galerie",
"sendingToGallery": "Envoi des générations à la galerie",
"disableTransparencyEffect": "Désactiver l'effet de transparence",
"HUD": {
"entityStatus": {
@@ -2097,16 +2007,11 @@
"opacity": "Opacité",
"savedToGalleryError": "Erreur lors de l'enregistrement dans la galerie",
"addInpaintMask": "Ajouter $t(controlLayers.inpaintMask)",
"newCanvasSessionDesc": "Cela effacera la toile et tous les paramètres, sauf votre sélection de modèle. Les générations seront mises en attente sur la toile.",
"canvas": "Toile",
"savedToGalleryOk": "Enregistré dans la galerie",
"addPositivePrompt": "Ajouter $t(controlLayers.prompt)",
"showProgressOnCanvas": "Afficher la progression sur la Toile",
"newGallerySession": "Nouvelle session de galerie",
"newCanvasSession": "Nouvelle session de toile",
"showingType": "Afficher {{type}}",
"viewProgressInViewer": "Voir les progrès et les résultats dans le <Btn>Visionneur d'images</Btn>.",
"deletePrompt": "Supprimer le prompt",
"addControlLayer": "Ajouter $t(controlLayers.controlLayer)",
"global": "Global",
"newGlobalReferenceImageOk": "Image de référence globale créée",
@@ -2120,16 +2025,6 @@
"newRasterLayerError": "Problème de création de couche de rastérisation",
"negativePrompt": "Prompt négatif",
"weight": "Poids",
"globalReferenceImages_withCount_hidden": "Images de référence globales ({{count}} cachées)",
"inpaintMasks_withCount_hidden": "Masques de remplissage ({{count}} cachés)",
"regionalGuidance_withCount_visible": "Guidage Régional ({{count}})",
"globalReferenceImage_withCount_one": "$t(controlLayers.globalReferenceImage)",
"globalReferenceImage_withCount_many": "Images de référence globales",
"globalReferenceImage_withCount_other": "Images de référence globales",
"layer_withCount_one": "Couche {{count}}",
"layer_withCount_many": "Couches {{count}}",
"layer_withCount_other": "Couches {{count}}",
"globalReferenceImages_withCount_visible": "Images de référence globales ({{count}})",
"controlMode": {
"controlMode": "Mode de contrôle",
"balanced": "Équilibré",
@@ -2153,18 +2048,14 @@
},
"fitBboxToLayers": "Ajuster la bounding box aux calques",
"regionIsEmpty": "La zone sélectionnée est vide",
"controlLayers_withCount_visible": "Couches de contrôle ({{count}})",
"cropLayerToBbox": "Rogner la couche selon la bounding box",
"sendingToCanvas": "Mise en attente des Générations sur la Toile",
"copyToClipboard": "Copier dans le presse-papiers",
"regionalGuidance_withCount_one": "$t(controlLayers.regionalGuidance)",
"regionalGuidance_withCount_many": "Guidage Régional",
"regionalGuidance_withCount_other": "Guidage Régional",
"newGallerySessionDesc": "Cela effacera la toile et tous les paramètres, sauf votre sélection de modèle. Les générations seront envoyées à la galerie.",
"inpaintMask_withCount_one": "$t(controlLayers.inpaintMask)",
"inpaintMask_withCount_many": "Remplir les masques",
"inpaintMask_withCount_other": "Remplir les masques",
"newImg2ImgCanvasFromImage": "Nouvelle Img2Img à partir de l'image",
"bboxOverlay": "Afficher la superposition des Bounding Box",
"moveToFront": "Déplacer vers le permier plan",
"moveToBack": "Déplacer vers l'arrière plan",
@@ -2179,7 +2070,6 @@
"inpaintMask": "Masque de remplissage",
"deleteReferenceImage": "Supprimer l'image de référence",
"addReferenceImage": "Ajouter $t(controlLayers.referenceImage)",
"addGlobalReferenceImage": "Ajouter $t(controlLayers.globalReferenceImage)",
"removeBookmark": "Supprimer le marque-page",
"regionalGuidance": "Guide régional",
"regionalReferenceImage": "Image de référence régionale",
@@ -2217,7 +2107,6 @@
"copyRasterLayerTo": "Copier $t(controlLayers.rasterLayer) vers",
"newControlLayer": "Nouveau $t(controlLayers.controlLayer)",
"newRegionalGuidance": "Nouveau $t(controlLayers.regionalGuidance)",
"replaceCurrent": "Remplacer Actuel",
"convertControlLayerTo": "Convertir $t(controlLayers.controlLayer) vers",
"convertInpaintMaskTo": "Convertir $t(controlLayers.inpaintMask) vers",
"copyControlLayerTo": "Copier $t(controlLayers.controlLayer) vers",
@@ -2263,9 +2152,7 @@
"pasteToBboxDesc": "Nouvelle couche (dans Bbox)",
"pasteToCanvasDesc": "Nouvelle couche (dans la Toile)",
"useImage": "Utiliser l'image",
"pastedTo": "Collé à {{destination}}",
"referenceImageEmptyState": "<UploadButton>Séléctionner une image</UploadButton> ou faites glisser une image depuis la <GalleryButton>galerie</GalleryButton> sur cette couche pour commencer.",
"referenceImageGlobal": "Image de référence (Globale)"
"referenceImageEmptyState": "<UploadButton>Séléctionner une image</UploadButton> ou faites glisser une image depuis la <GalleryButton>galerie</GalleryButton> sur cette couche pour commencer."
},
"upscaling": {
"exceedsMaxSizeDetails": "La limite maximale d'agrandissement est de {{maxUpscaleDimension}}x{{maxUpscaleDimension}} pixels. Veuillez essayer une image plus petite ou réduire votre sélection d'échelle.",

View File

@@ -50,8 +50,7 @@
"gallery": {
"galleryImageSize": "גודל תמונה",
"gallerySettings": "הגדרות גלריה",
"autoSwitchNewImages": "החלף אוטומטית לתמונות חדשות",
"noImagesInGallery": "אין תמונות בגלריה"
"autoSwitchNewImages": "החלף אוטומטית לתמונות חדשות"
},
"parameters": {
"images": "תמונות",
@@ -70,12 +69,10 @@
"tileSize": "גודל אריח",
"symmetry": "סימטריה",
"copyImage": "העתקת תמונה",
"downloadImage": "הורדת תמונה",
"usePrompt": "שימוש בבקשה",
"useSeed": "שימוש בזרע",
"useAll": "שימוש בהכל",
"info": "פרטים",
"showOptionsPanel": "הצג חלונית אפשרויות",
"shuffle": "ערבוב",
"noiseThreshold": "סף רעש",
"perlinNoise": "רעש פרלין",

View File

@@ -26,7 +26,6 @@
"modelManager": "Gestione Modelli",
"communityLabel": "Comunità",
"advanced": "Avanzate",
"imageFailedToLoad": "Impossibile caricare l'immagine",
"learnMore": "Per saperne di più",
"ipAdapter": "Adattatore IP",
"t2iAdapter": "Adattatore T2I",
@@ -45,22 +44,18 @@
"somethingWentWrong": "Qualcosa è andato storto",
"copyError": "Errore $t(gallery.copy)",
"input": "Ingresso",
"notInstalled": "Non $t(common.installed)",
"unknownError": "Errore sconosciuto",
"updated": "Aggiornato",
"save": "Salva",
"created": "Creato",
"prevPage": "Pagina precedente",
"delete": "Elimina",
"orderBy": "Ordina per",
"nextPage": "Pagina successiva",
"saveAs": "Salva come",
"direction": "Direzione",
"or": "o",
"red": "Rosso",
"aboutHeading": "Possiedi il tuo potere creativo",
"aboutDesc": "Utilizzi Invoke per lavoro? Guarda qui:",
"localSystem": "Sistema locale",
"green": "Verde",
"blue": "Blu",
"alpha": "Alfa",
@@ -76,7 +71,6 @@
"positivePrompt": "Prompt positivo",
"negativePrompt": "Prompt negativo",
"selected": "Selezionato",
"goTo": "Vai a",
"editor": "Editor",
"tab": "Scheda",
"enabled": "Abilitato",
@@ -102,7 +96,6 @@
"values": "Valori",
"start": "Inizio",
"end": "Fine",
"resetToDefaults": "Ripristina le impostazioni predefinite",
"seed": "Seme",
"combinatorial": "Combinatorio",
"count": "Quantità",
@@ -137,7 +130,6 @@
"galleryImageSize": "Dimensione dell'immagine",
"gallerySettings": "Impostazioni della galleria",
"autoSwitchNewImages": "Passaggio automatico a nuove immagini",
"noImagesInGallery": "Nessuna immagine da visualizzare",
"deleteImage_one": "Elimina l'immagine",
"deleteImage_many": "Elimina {{count}} immagini",
"deleteImage_other": "Elimina {{count}} immagini",
@@ -145,7 +137,6 @@
"autoAssignBoardOnClick": "Assegna automaticamente la bacheca al clic",
"featuresWillReset": "Se elimini questa immagine, quelle funzionalità verranno immediatamente ripristinate.",
"loading": "Caricamento in corso",
"unableToLoad": "Impossibile caricare la Galleria",
"currentlyInUse": "Questa immagine è attualmente utilizzata nelle seguenti funzionalità:",
"copy": "Copia",
"download": "Scarica",
@@ -154,9 +145,9 @@
"deleteSelection": "Elimina la selezione",
"image": "immagine",
"drop": "Rilascia",
"unstarImage": "Rimuovi contrassegno immagine",
"unstarImage": "Rimuovi contrassegno",
"dropOrUpload": "Rilascia o carica",
"starImage": "Contrassegna l'immagine",
"starImage": "Contrassegna",
"dropToUpload": "$t(gallery.drop) per aggiornare",
"bulkDownloadRequested": "Preparazione del download",
"bulkDownloadRequestedDesc": "La tua richiesta di download è in preparazione. L'operazione potrebbe richiedere alcuni istanti.",
@@ -165,7 +156,6 @@
"alwaysShowImageSizeBadge": "Mostra sempre le dimensioni dell'immagine",
"openInViewer": "Apri nel visualizzatore",
"selectForCompare": "Seleziona per il confronto",
"selectAnImageToCompare": "Seleziona un'immagine da confrontare",
"slider": "Cursore",
"sideBySide": "Fianco a Fianco",
"compareImage": "Immagine di confronto",
@@ -190,18 +180,21 @@
"exitBoardSearch": "Esci da Ricerca bacheca",
"exitSearch": "Esci dalla ricerca immagini",
"go": "Vai",
"jump": "Salta",
"move": "Sposta",
"gallery": "Galleria",
"openViewer": "Apri visualizzatore",
"closeViewer": "Chiudi visualizzatore",
"imagesTab": "Immagini create e salvate in Invoke.",
"assetsTab": "File che hai caricato per usarli nei tuoi progetti.",
"boardsSettings": "Impostazioni Bacheche",
"imagesSettings": "Impostazioni Immagini Galleria",
"assets": "Risorse",
"images": "Immagini",
"useForPromptGeneration": "Usa per generare il prompt"
"useForPromptGeneration": "Usa per generare il prompt",
"deleteVideo_one": "Elimina video",
"deleteVideo_many": "Elimina {{count}} video",
"deleteVideo_other": "Elimina {{count}} video",
"deleteVideoPermanent": "I video eliminati non possono essere ripristinati.",
"videos": "Video",
"videosTab": "Video creati e salvati in Invoke."
},
"hotkeys": {
"searchHotkeys": "Cerca tasti di scelta rapida",
@@ -267,6 +260,10 @@
"selectGenerateTab": {
"title": "Seleziona la scheda Genera",
"desc": "Seleziona la scheda Genera."
},
"selectVideoTab": {
"title": "Seleziona la scheda Video",
"desc": "Seleziona la scheda Video."
}
},
"hotkeys": "Tasti di scelta rapida",
@@ -347,10 +344,6 @@
"title": "Livello precedente",
"desc": "Seleziona il livello precedente nell'elenco."
},
"setFillToWhite": {
"title": "Imposta il colore su bianco",
"desc": "Imposta il colore dello strumento corrente su bianco."
},
"title": "Tela",
"selectMoveTool": {
"title": "Strumento Sposta",
@@ -421,6 +414,14 @@
"toggleBbox": {
"title": "Attiva/disattiva la visibilità del riquadro di delimitazione",
"desc": "Nascondi o mostra il riquadro di delimitazione della generazione"
},
"setFillColorsToDefault": {
"title": "Imposta i colori come predefiniti",
"desc": "Imposta i colori degli strumenti correnti sui valori predefiniti."
},
"toggleFillColor": {
"title": "Attiva/disattiva colore di riempimento",
"desc": "Attiva/disattiva il colore di riempimento dello strumento corrente."
}
},
"workflows": {
@@ -555,6 +556,9 @@
"desc": "Aggiungi/Rimuovi contrassegno all'immagine selezionata.",
"title": "Aggiungi / Rimuovi contrassegno immagine"
}
},
"video": {
"title": "Video"
}
},
"modelManager": {
@@ -655,9 +659,7 @@
"noModelsInstalled": "Nessun modello installato",
"main": "Principali",
"noModelsInstalledDesc1": "Installa i modelli con",
"ipAdapters": "Adattatori IP",
"noMatchingModels": "Nessun modello corrispondente",
"starterModelsInModelManager": "I modelli iniziali possono essere trovati in Gestione Modelli",
"spandrelImageToImage": "Immagine a immagine (Spandrel)",
"learnMoreAboutSupportedModels": "Scopri di più sui modelli che supportiamo",
"starterBundles": "Pacchetti per iniziare",
@@ -696,13 +698,11 @@
"urlForbiddenErrorMessage": "Potrebbe essere necessario richiedere l'autorizzazione al sito che distribuisce il modello.",
"urlUnauthorizedErrorMessage": "Potrebbe essere necessario configurare un gettone API per accedere a questo modello.",
"fileSize": "Dimensione del file",
"filterModels": "Filtra i modelli",
"modelPickerFallbackNoModelsInstalled": "Nessun modello installato.",
"modelPickerFallbackNoModelsInstalled2": "Visita <LinkComponent>Gestione modelli</LinkComponent> per installare i modelli.",
"manageModels": "Gestione modelli",
"hfTokenReset": "Ripristino del gettone HF",
"relatedModels": "Modelli correlati",
"showOnlyRelatedModels": "Correlati",
"installedModelsCount": "{{installed}} di {{total}} modelli installati.",
"allNModelsInstalled": "Tutti i {{count}} modelli installati",
"nToInstall": "{{count}} da installare",
@@ -718,9 +718,7 @@
"recommendedModels": "Modelli consigliati",
"exploreStarter": "Oppure sfoglia tutti i modelli iniziali disponibili",
"welcome": "Benvenuti in Gestione Modelli",
"quickStart": "Pacchetti di avvio rapido",
"bundleDescription": "Ogni pacchetto include modelli essenziali per ogni famiglia di modelli e modelli base selezionati per iniziare.",
"browseAll": "Oppure scopri tutti i modelli disponibili:"
"bundleDescription": "Ogni pacchetto include modelli essenziali per ogni famiglia di modelli e modelli base selezionati per iniziare."
},
"launchpadTab": "Rampa di lancio",
"installBundle": "Installa pacchetto",
@@ -747,12 +745,10 @@
"scaledHeight": "Altezza scalata",
"infillMethod": "Metodo di riempimento",
"tileSize": "Dimensione piastrella",
"downloadImage": "Scarica l'immagine",
"usePrompt": "Usa Prompt",
"useSeed": "Usa Seme",
"useAll": "Usa Tutto",
"info": "Informazioni",
"showOptionsPanel": "Mostra il pannello laterale (O o T)",
"general": "Generale",
"denoisingStrength": "Forza di riduzione del rumore",
"copyImage": "Copia immagine",
@@ -779,10 +775,6 @@
"missingNodeTemplate": "Modello di nodo mancante",
"missingInputForField": "ingresso mancante",
"missingFieldTemplate": "Modello di campo mancante",
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), altezza riquadro è {{height}}",
"fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), larghezza riquadro è {{width}}",
"fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), larghezza del riquadro scalato è {{width}}",
"fluxModelIncompatibleScaledBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), altezza del riquadro scalato è {{height}}",
"noT5EncoderModelSelected": "Nessun modello di encoder T5 selezionato per la generazione con FLUX",
"noCLIPEmbedModelSelected": "Nessun modello CLIP Embed selezionato per la generazione con FLUX",
"noFLUXVAEModelSelected": "Nessun modello VAE selezionato per la generazione con FLUX",
@@ -798,7 +790,6 @@
"collectionStringTooLong": "troppo lungo, massimo {{maxLength}}",
"batchNodeNotConnected": "Nodo Lotto non connesso: {{label}}",
"batchNodeEmptyCollection": "Alcuni nodi lotto hanno raccolte vuote",
"emptyBatches": "lotti vuoti",
"batchNodeCollectionSizeMismatch": "Le dimensioni della raccolta nel Lotto {{batchGroupId}} non corrispondono",
"collectionStringTooShort": "troppo corto, minimo {{minLength}}",
"collectionNumberNotMultipleOf": "{{value}} non è multiplo di {{multipleOf}}",
@@ -812,9 +803,11 @@
"modelIncompatibleScaledBboxWidth": "La larghezza scalata del riquadro è {{width}} ma {{model}} richiede multipli di {{multiple}}",
"modelIncompatibleScaledBboxHeight": "L'altezza scalata del riquadro è {{height}} ma {{model}} richiede multipli di {{multiple}}",
"modelDisabledForTrial": "La generazione con {{modelName}} non è disponibile per gli account di prova. Accedi alle impostazioni del tuo account per effettuare l'upgrade.",
"fluxKontextMultipleReferenceImages": "È possibile utilizzare solo 1 immagine di riferimento alla volta con FLUX Kontext tramite BFL API",
"promptExpansionResultPending": "Accetta o ignora il risultato dell'espansione del prompt",
"promptExpansionPending": "Espansione del prompt in corso"
"promptExpansionPending": "Espansione del prompt in corso",
"noStartingFrameImage": "Nessuna immagine del fotogramma iniziale",
"videoIsDisabled": "La generazione di video non è abilitata per gli account {{accountType}}.",
"incompatibleLoRAs": "Aggiunti LoRA incompatibili"
},
"useCpuNoise": "Usa la CPU per generare rumore",
"iterations": "Iterazioni",
@@ -843,7 +836,19 @@
"recallMetadata": "Richiama i metadati",
"disabledNoRasterContent": "Disabilitato (nessun contenuto Raster)",
"modelDisabledForTrial": "La generazione con {{modelName}} non è disponibile per gli account di prova. Visita le <LinkComponent>impostazioni account</LinkComponent> per effettuare l'upgrade.",
"useClipSkip": "Usa CLIP Skip"
"useClipSkip": "Usa CLIP Skip",
"duration": "Durata",
"images_withCount_one": "Immagine",
"images_withCount_many": "Immagini",
"images_withCount_other": "Immagini",
"videos_withCount_one": "Video",
"videos_withCount_many": "Video",
"videos_withCount_other": "Video",
"startingFrameImage": "Fotogramma iniziale",
"videoActions": "Azioni video",
"sendToVideo": "Invia al Video",
"video": "Video",
"resolution": "Risoluzione"
},
"settings": {
"models": "Modelli",
@@ -880,8 +885,6 @@
"informationalPopoversDisabledDesc": "I testi informativi a comparsa sono disabilitati. Attivali nelle impostazioni.",
"confirmOnNewSession": "Conferma su nuova sessione",
"enableModelDescriptions": "Abilita le descrizioni dei modelli nei menu a discesa",
"modelDescriptionsDisabled": "Descrizioni dei modelli nei menu a discesa disabilitate",
"modelDescriptionsDisabledDesc": "Le descrizioni dei modelli nei menu a discesa sono state disabilitate. Abilitale nelle Impostazioni.",
"showDetailedInvocationProgress": "Mostra dettagli avanzamento",
"enableHighlightFocusedRegions": "Evidenzia le regioni interessate"
},
@@ -896,18 +899,15 @@
"parameterSet": "Parametro richiamato",
"parameterNotSet": "Parametro non richiamato",
"problemCopyingImage": "Impossibile copiare l'immagine",
"baseModelChangedCleared_one": "Cancellato o disabilitato {{count}} sottomodello incompatibile",
"baseModelChangedCleared_many": "Cancellati o disabilitati {{count}} sottomodelli incompatibili",
"baseModelChangedCleared_one": "Aggiornato, cancellato o disabilitato {{count}} sottomodello incompatibile",
"baseModelChangedCleared_many": "Aggiornati, cancellati o disabilitati {{count}} sottomodelli incompatibili",
"baseModelChangedCleared_other": "Cancellati o disabilitati {{count}} sottomodelli incompatibili",
"loadedWithWarnings": "Flusso di lavoro caricato con avvisi",
"imageUploaded": "Immagine caricata",
"addedToBoard": "Aggiunto alle risorse della bacheca {{name}}",
"modelAddedSimple": "Modello aggiunto alla Coda",
"imageUploadFailed": "Caricamento immagine non riuscito",
"setControlImage": "Imposta come immagine di controllo",
"setNodeField": "Imposta come campo nodo",
"workflowLoaded": "Flusso di lavoro caricato",
"invalidUpload": "Caricamento non valido",
"problemDeletingWorkflow": "Problema durante l'eliminazione del flusso di lavoro",
"workflowDeleted": "Flusso di lavoro eliminato",
"problemRetrievingWorkflow": "Problema nel recupero del flusso di lavoro",
@@ -927,7 +927,6 @@
"outOfMemoryErrorDesc": "Le impostazioni della generazione attuale superano la capacità del sistema. Modifica le impostazioni e riprova.",
"importFailed": "Importazione non riuscita",
"importSuccessful": "Importazione riuscita",
"layerSavedToAssets": "Livello salvato nelle risorse",
"problemSavingLayer": "Impossibile salvare il livello",
"unableToLoadImage": "Impossibile caricare l'immagine",
"problemCopyingLayer": "Impossibile copiare il livello",
@@ -936,16 +935,10 @@
"unableToLoadStylePreset": "Impossibile caricare lo stile predefinito",
"stylePresetLoaded": "Stile predefinito caricato",
"unableToLoadImageMetadata": "Impossibile caricare i metadati dell'immagine",
"imageSaved": "Immagine salvata",
"imageSavingFailed": "Salvataggio dell'immagine non riuscito",
"layerCopiedToClipboard": "Livello copiato negli appunti",
"imageNotLoadedDesc": "Impossibile trovare l'immagine",
"linkCopied": "Collegamento copiato",
"addedToUncategorized": "Aggiunto alle risorse della bacheca $t(boards.uncategorized)",
"imagesWillBeAddedTo": "Le immagini caricate verranno aggiunte alle risorse della bacheca {{boardName}}.",
"uploadFailedInvalidUploadDesc_withCount_one": "Devi caricare al massimo 1 immagine PNG, JPEG o WEBP.",
"uploadFailedInvalidUploadDesc_withCount_many": "Devi caricare al massimo {{count}} immagini PNG, JPEG o WEBP.",
"uploadFailedInvalidUploadDesc_withCount_other": "Devi caricare al massimo {{count}} immagini PNG, JPEG o WEBP.",
"outOfMemoryErrorDescLocal": "Segui la nostra <LinkComponent>guida per bassa VRAM</LinkComponent> per ridurre gli OOM.",
"pasteFailed": "Incolla non riuscita",
"pasteSuccess": "Incollato su {{destination}}",
@@ -958,29 +951,19 @@
"workflowUnpublished": "Flusso di lavoro non pubblicato",
"chatGPT4oIncompatibleGenerationMode": "ChatGPT 4o supporta solo la conversione da testo a immagine e da immagine a immagine. Utilizza altri modelli per le attività di Inpainting e Outpainting.",
"imagenIncompatibleGenerationMode": "Google {{model}} supporta solo la generazione da testo a immagine. Utilizza altri modelli per le attività di conversione da immagine a immagine, inpainting e outpainting.",
"noRasterLayers": "Nessun livello raster trovato",
"noRasterLayersDesc": "Crea almeno un livello raster da esportare in PSD",
"noActiveRasterLayers": "Nessun livello raster attivo",
"noActiveRasterLayersDesc": "Abilitare almeno un livello raster da esportare in PSD",
"noVisibleRasterLayers": "Nessun livello raster visibile",
"noVisibleRasterLayersDesc": "Abilitare almeno un livello raster da esportare in PSD",
"invalidCanvasDimensions": "Dimensioni della tela non valide",
"canvasTooLarge": "Tela troppo grande",
"canvasTooLargeDesc": "Le dimensioni della tela superano le dimensioni massime consentite per l'esportazione in formato PSD. Riduci la larghezza e l'altezza totali della tela e riprova.",
"failedToProcessLayers": "Impossibile elaborare i livelli",
"psdExportSuccess": "Esportazione PSD completata",
"psdExportSuccessDesc": "Esportazione riuscita di {{count}} livelli nel file PSD",
"problemExportingPSD": "Problema durante l'esportazione PSD",
"noValidLayerAdapters": "Nessun adattatore di livello valido trovato",
"fluxKontextIncompatibleGenerationMode": "FLUX Kontext non supporta la generazione di immagini posizionate sulla tela. Riprova utilizzando la sezione Immagine di riferimento e disattiva tutti i livelli raster.",
"canvasManagerNotAvailable": "Gestione tela non disponibile",
"promptExpansionFailed": "Abbiamo riscontrato un problema. Riprova a eseguire l'espansione del prompt.",
"uploadAndPromptGenerationFailed": "Impossibile caricare l'immagine e generare il prompt",
"promptGenerationStarted": "Generazione del prompt avviata",
"invalidBboxDesc": "Il riquadro di delimitazione non ha dimensioni valide",
"invalidBbox": "Riquadro di delimitazione non valido",
"noInpaintMaskSelectedDesc": "Seleziona una maschera di inpaint da invertire",
"noInpaintMaskSelected": "Nessuna maschera di inpaint selezionata",
"noVisibleMasksDesc": "Crea o abilita almeno una maschera inpaint da invertire",
"noVisibleMasks": "Nessuna maschera visibile",
"maskInvertFailed": "Impossibile invertire la maschera",
@@ -1004,8 +987,6 @@
},
"nodes": {
"zoomOutNodes": "Rimpicciolire",
"hideLegendNodes": "Nascondi la legenda del tipo di campo",
"showLegendNodes": "Mostra legenda del tipo di campo",
"hideMinimapnodes": "Nascondi minimappa",
"showMinimapnodes": "Mostra minimappa",
"zoomInNodes": "Ingrandire",
@@ -1031,8 +1012,6 @@
"workflowSettings": "Impostazioni Editor del flusso di lavoro",
"colorCodeEdges": "Bordi con codice colore",
"noOutputRecorded": "Nessun output registrato",
"noFieldsLinearview": "Nessun campo aggiunto alla vista lineare",
"removeLinearView": "Rimuovi dalla vista lineare",
"workflowDescription": "Breve descrizione",
"workflowContact": "Contatto",
"workflowVersion": "Versione",
@@ -1043,15 +1022,12 @@
"workflowAuthor": "Autore",
"workflowName": "Nome",
"workflowNotes": "Note",
"versionUnknown": " Versione sconosciuta",
"unableToValidateWorkflow": "Impossibile convalidare il flusso di lavoro",
"updateApp": "Aggiorna Applicazione",
"unableToLoadWorkflow": "Impossibile caricare il flusso di lavoro",
"updateNode": "Aggiorna nodo",
"version": "Versione",
"notes": "Note",
"problemSettingTitle": "Problema nell'impostazione del titolo",
"unknownTemplate": "Modello sconosciuto",
"nodeType": "Tipo di nodo",
"notesDescription": "Aggiunge note sul tuo flusso di lavoro",
"unknownField": "Campo sconosciuto",
@@ -1070,7 +1046,6 @@
"cannotConnectInputToInput": "Impossibile collegare ingresso a ingresso",
"cannotConnectOutputToOutput": "Impossibile collegare uscita ad uscita",
"cannotConnectToSelf": "Impossibile connettersi a se stesso",
"mismatchedVersion": "Nodo non valido: il nodo {{node}} di tipo {{type}} ha una versione non corrispondente (provare ad aggiornare?)",
"loadingNodes": "Caricamento nodi...",
"enum": "Enumeratore",
"float": "In virgola mobile",
@@ -1087,7 +1062,6 @@
"unableToUpdateNodes_one": "Impossibile aggiornare {{count}} nodo",
"unableToUpdateNodes_many": "Impossibile aggiornare {{count}} nodi",
"unableToUpdateNodes_other": "Impossibile aggiornare {{count}} nodi",
"addLinearView": "Aggiungi alla vista Lineare",
"unknownErrorValidatingWorkflow": "Errore sconosciuto durante la convalida del flusso di lavoro",
"collectionFieldType": "{{name}} (Raccolta)",
"collectionOrScalarFieldType": "{{name}} (Singola o Raccolta)",
@@ -1109,7 +1083,6 @@
"targetNodeDoesNotExist": "Connessione non valida: il nodo di destinazione/input {{node}} non esiste",
"unknownFieldType": "$t(nodes.unknownField) tipo: {{type}}",
"deletedInvalidEdge": "Eliminata connessione non valida {{source}} -> {{target}}",
"unknownInput": "Input sconosciuto: {{name}}",
"prototypeDesc": "Questa invocazione è un prototipo. Potrebbe subire modifiche sostanziali durante gli aggiornamenti dell'app e potrebbe essere rimossa in qualsiasi momento.",
"betaDesc": "Questa invocazione è in versione beta. Fino a quando non sarà stabile, potrebbe subire modifiche importanti durante gli aggiornamenti dell'app. Abbiamo intenzione di supportare questa invocazione a lungo termine.",
"newWorkflow": "Nuovo flusso di lavoro",
@@ -1120,7 +1093,6 @@
"clearWorkflow": "Cancella il flusso di lavoro",
"clearWorkflowDesc2": "Il tuo flusso di lavoro attuale presenta modifiche non salvate.",
"viewMode": "Usa la vista lineare",
"reorderLinearView": "Riordina la vista lineare",
"editMode": "Modifica nell'editor del flusso di lavoro",
"resetToDefaultValue": "Ripristina il valore predefinito",
"noFieldsViewMode": "Questo flusso di lavoro non ha campi selezionati da visualizzare. Visualizza il flusso di lavoro completo per configurare i valori.",
@@ -1144,18 +1116,14 @@
"specialDesc": "Questa invocazione comporta una gestione speciale nell'applicazione. Ad esempio, i nodi Lotto vengono utilizzati per mettere in coda più grafici da un singolo flusso di lavoro.",
"internalDesc": "Questa invocazione è utilizzata internamente da Invoke. Potrebbe subire modifiche significative durante gli aggiornamenti dell'app e potrebbe essere rimossa in qualsiasi momento.",
"addItem": "Aggiungi elemento",
"generateValues": "Genera valori",
"generatorNoValues": "vuoto",
"linearDistribution": "Distribuzione lineare",
"parseString": "Analizza stringa",
"splitOn": "Diviso su",
"noBatchGroup": "nessun gruppo",
"generatorLoading": "caricamento",
"generatorLoadFromFile": "Carica da file",
"dynamicPromptsRandom": "Prompt dinamici (casuali)",
"dynamicPromptsCombinatorial": "Prompt dinamici (combinatori)",
"floatRangeGenerator": "Generatore di intervalli di numeri in virgola mobile",
"integerRangeGenerator": "Generatore di intervalli di numeri interi",
"uniformRandomDistribution": "Distribuzione casuale uniforme",
"generatorNRandomValues_one": "{{count}} valore casuale",
"generatorNRandomValues_many": "{{count}} valori casuali",
@@ -1166,7 +1134,6 @@
"loadWorkflowDesc2": "Il flusso di lavoro corrente presenta modifiche non salvate.",
"downloadWorkflowError": "Errore durante lo scaricamento del flusso di lavoro",
"deletedMissingNodeFieldFormElement": "Campo modulo mancante eliminato: nodo {{nodeId}} campo {{fieldName}}",
"loadingTemplates": "Caricamento {{name}}",
"unableToUpdateNode": "Aggiornamento del nodo non riuscito: nodo {{node}} di tipo {{type}} (potrebbe essere necessario eliminarlo e ricrearlo)",
"description": "Descrizione",
"generatorImagesCategory": "Categoria",
@@ -1236,14 +1203,17 @@
"shared": "Bacheche condivise",
"addPrivateBoard": "Aggiungi una Bacheca Privata",
"noBoards": "Nessuna bacheca {{boardType}}",
"hideBoards": "Nascondi bacheche",
"viewBoards": "Visualizza bacheche",
"deletedPrivateBoardsCannotbeRestored": "Le bacheche e le immagini eliminate non possono essere ripristinate. Selezionando \"Elimina solo bacheca\", le immagini verranno spostate in uno stato privato e non categorizzato per l'autore dell'immagine.",
"updateBoardError": "Errore durante l'aggiornamento della bacheca",
"uncategorizedImages": "Immagini non categorizzate",
"deleteAllUncategorizedImages": "Elimina tutte le immagini non categorizzate",
"deletedImagesCannotBeRestored": "Le immagini eliminate non possono essere ripristinate.",
"locateInGalery": "Trova nella Galleria"
"locateInGalery": "Trova nella Galleria",
"movingVideosToBoard_one": "Spostamento di {{count}} video sulla bacheca:",
"movingVideosToBoard_many": "Spostamento di {{count}} video sulla bacheca:",
"movingVideosToBoard_other": "Spostamento di {{count}} video sulla bacheca:",
"videosWithCount_one": "{{count}} video",
"videosWithCount_many": "{{count}} video",
"videosWithCount_other": "{{count}} video"
},
"queue": {
"queueFront": "Aggiungi all'inizio della coda",
@@ -1334,11 +1304,9 @@
"models": {
"noMatchingModels": "Nessun modello corrispondente",
"loading": "caricamento",
"noMatchingLoRAs": "Nessun LoRA corrispondente",
"noModelsAvailable": "Nessun modello disponibile",
"selectModel": "Seleziona un modello",
"noRefinerModelsInstalled": "Nessun modello affinatore SDXL installato",
"noLoRAsInstalled": "Nessun LoRA installato",
"addLora": "Aggiungi LoRA",
"defaultVAE": "VAE predefinito",
"concepts": "Concetti",
@@ -1374,8 +1342,7 @@
"dynamicPrompts": "Prompt dinamici",
"promptsPreview": "Anteprima dei prompt",
"showDynamicPrompts": "Mostra prompt dinamici",
"loading": "Generazione prompt dinamici...",
"promptsToGenerate": "Prompt da generare"
"loading": "Generazione prompt dinamici..."
},
"popovers": {
"paramScheduler": {
@@ -1821,18 +1788,14 @@
"scheduler": "Campionatore",
"noModelsAvailable": "Nessun modello disponibile",
"denoisingStrength": "Forza di riduzione del rumore",
"concatPromptStyle": "Collega Prompt & Stile",
"loading": "Caricamento...",
"steps": "Passi",
"refinerStart": "Inizio Affinamento",
"cfgScale": "Scala CFG",
"negStylePrompt": "Prompt Stile negativo",
"refiner": "Affinatore",
"negAestheticScore": "Punteggio estetico negativo",
"refinermodel": "Modello Affinatore",
"posAestheticScore": "Punteggio estetico positivo",
"posStylePrompt": "Prompt Stile positivo",
"freePromptStyle": "Prompt di stile manuale",
"refinerSteps": "Passi Affinamento"
},
"metadata": {
@@ -1860,17 +1823,19 @@
"allPrompts": "Tutti i prompt",
"imageDimensions": "Dimensioni dell'immagine",
"parameterSet": "Parametro {{parameter}} impostato",
"parsingFailed": "Analisi non riuscita",
"recallParameter": "Richiama {{label}}",
"canvasV2Metadata": "Livelli Tela",
"guidance": "Guida",
"seamlessXAxis": "Asse X senza giunte",
"seamlessYAxis": "Asse Y senza giunte",
"vae": "VAE"
"vae": "VAE",
"videoDetails": "Dettagli video",
"noVideoDetails": "Nessun dettaglio video trovato",
"videoModel": "Modello",
"videoDuration": "Durata",
"videoAspectRatio": "Proporzioni",
"videoResolution": "Risoluzione"
},
"hrf": {
"enableHrf": "Abilita Correzione Alta Risoluzione",
"upscaleMethod": "Metodo di ampliamento",
"metadata": {
"strength": "Forza della Correzione Alta Risoluzione",
"enabled": "Correzione Alta Risoluzione Abilitata",
@@ -1883,19 +1848,14 @@
"workflowEditorMenu": "Menu dell'editor del flusso di lavoro",
"workflowName": "Nome del flusso di lavoro",
"saveWorkflow": "Salva flusso di lavoro",
"openWorkflow": "Apri flusso di lavoro",
"clearWorkflowSearchFilter": "Cancella il filtro di ricerca del flusso di lavoro",
"workflowLibrary": "Libreria flussi di lavoro",
"workflowSaved": "Flusso di lavoro salvato",
"unnamedWorkflow": "Flusso di lavoro senza nome",
"savingWorkflow": "Salvataggio del flusso di lavoro...",
"problemLoading": "Problema durante il caricamento dei flussi di lavoro",
"loading": "Caricamento dei flussi di lavoro",
"searchWorkflows": "Cerca flussi di lavoro",
"problemSavingWorkflow": "Problema durante il salvataggio del flusso di lavoro",
"deleteWorkflow": "Elimina flusso di lavoro",
"workflows": "Flussi di lavoro",
"noDescription": "Nessuna descrizione",
"newWorkflowCreated": "Nuovo flusso di lavoro creato",
"downloadWorkflow": "Salva su file",
"uploadWorkflow": "Carica da file",
@@ -1912,9 +1872,6 @@
"loadWorkflow": "$t(common.load) Flusso di lavoro",
"autoLayout": "Schema automatico",
"loadFromGraph": "Carica il flusso di lavoro dal grafico",
"userWorkflows": "Flussi di lavoro utente",
"projectWorkflows": "Flussi di lavoro del progetto",
"defaultWorkflows": "Flussi di lavoro predefiniti",
"chooseWorkflowFromLibrary": "Scegli il flusso di lavoro dalla libreria",
"deleteWorkflow2": "Vuoi davvero eliminare questo flusso di lavoro? Questa operazione non può essere annullata.",
"edit": "Modifica",
@@ -1922,7 +1879,6 @@
"copyShareLink": "Copia Condividi Link",
"copyShareLinkForWorkflow": "Copia Condividi Link del Flusso di lavoro",
"delete": "Elimina",
"openLibrary": "Apri la libreria",
"builder": {
"resetAllNodeFields": "Reimposta tutti i campi del nodo",
"row": "Riga",
@@ -1944,9 +1900,7 @@
"singleLine": "Linea singola",
"multiLine": "Linea Multipla",
"both": "Entrambi",
"emptyRootPlaceholderViewMode": "Fare clic su Modifica per iniziare a creare un modulo per questo flusso di lavoro.",
"textPlaceholder": "Testo vuoto",
"workflowBuilderAlphaWarning": "Il generatore di flussi di lavoro è attualmente in versione alpha. Potrebbero esserci cambiamenti radicali prima della versione stabile.",
"heading": "Intestazione",
"divider": "Divisore",
"container": "Contenitore",
@@ -1997,17 +1951,14 @@
},
"loadMore": "Carica altro",
"searchPlaceholder": "Cerca per nome, descrizione o etichetta",
"filterByTags": "Filtra per etichetta",
"shared": "Condiviso",
"browseWorkflows": "Sfoglia i flussi di lavoro",
"allLoaded": "Tutti i flussi di lavoro caricati",
"saveChanges": "Salva modifiche",
"yourWorkflows": "I tuoi flussi di lavoro",
"recentlyOpened": "Aperto di recente",
"workflowThumbnail": "Miniatura del flusso di lavoro",
"private": "Privato",
"deselectAll": "Deseleziona tutto",
"noRecentWorkflows": "Nessun flusso di lavoro recente",
"view": "Visualizza",
"recommended": "Consigliato per te",
"emptyStringPlaceholder": "<stringa vuota>",
@@ -2038,10 +1989,7 @@
"addPromptTrigger": "Aggiungi Trigger nel prompt",
"noMatchingTriggers": "Nessun Trigger corrispondente",
"discard": "Scarta",
"insert": "Inserisci",
"replace": "Sostituisci",
"resultSubtitle": "Scegli come gestire il prompt espanso:",
"resultTitle": "Espansione del prompt completata",
"expandingPrompt": "Espansione del prompt...",
"uploadImageForPromptGeneration": "Carica l'immagine per la generazione del prompt",
"expandCurrentPrompt": "Espandi il prompt corrente",
@@ -2054,7 +2002,6 @@
"moveForward": "Sposta avanti",
"moveBackward": "Sposta indietro",
"autoNegative": "Auto Negativo",
"deletePrompt": "Cancella il prompt",
"rectangle": "Rettangolo",
"addPositivePrompt": "Aggiungi $t(controlLayers.prompt)",
"addNegativePrompt": "Aggiungi $t(controlLayers.negativePrompt)",
@@ -2071,10 +2018,8 @@
"newCanvasFromImage": "Nuova tela da immagine",
"globalReferenceImage": "Immagine di riferimento Globale",
"copyToClipboard": "Copia negli appunti",
"sendingToCanvas": "Effettua le generazioni nella Tela",
"clearHistory": "Cancella la cronologia",
"inpaintMask": "Maschera Inpaint",
"sendToGallery": "Invia alla Galleria",
"controlLayer": "Livello di Controllo",
"rasterLayer_withCount_one": "$t(controlLayers.rasterLayer)",
"rasterLayer_withCount_many": "Livelli Raster",
@@ -2088,7 +2033,6 @@
"addControlLayer": "Aggiungi $t(controlLayers.controlLayer)",
"addInpaintMask": "Aggiungi $t(controlLayers.inpaintMask)",
"addRegionalGuidance": "Aggiungi $t(controlLayers.regionalGuidance)",
"sendToCanvasDesc": "Premendo Invoke il lavoro in corso viene visualizzato sulla tela.",
"addRasterLayer": "Aggiungi $t(controlLayers.rasterLayer)",
"clearCaches": "Svuota le cache",
"regionIsEmpty": "La regione selezionata è vuota",
@@ -2109,12 +2053,7 @@
"showHUD": "Mostra HUD",
"maskFill": "Riempimento maschera",
"addReferenceImage": "Aggiungi $t(controlLayers.referenceImage)",
"addGlobalReferenceImage": "Aggiungi $t(controlLayers.globalReferenceImage)",
"sendingToGallery": "Inviare generazioni alla Galleria",
"sendToGalleryDesc": "Premendo Invoke viene generata e salvata un'immagine unica nella tua galleria.",
"sendToCanvas": "Invia alla Tela",
"viewProgressInViewer": "Visualizza i progressi e i risultati nel <Btn>Visualizzatore immagini</Btn>.",
"viewProgressOnCanvas": "Visualizza i progressi e i risultati nella <Btn>Tela</Btn>.",
"saveBboxToGallery": "Salva il riquadro di delimitazione nella Galleria",
"cropLayerToBbox": "Ritaglia il livello al riquadro di delimitazione",
"savedToGalleryError": "Errore durante il salvataggio nella galleria",
@@ -2136,9 +2075,6 @@
"pullBboxIntoReferenceImageOk": "Contenuto del riquadro inserito nell'immagine di riferimento",
"pullBboxIntoLayerOk": "Riquadro caricato nel livello",
"pullBboxIntoReferenceImageError": "Problema nell'inserimento del contenuto del riquadro nell'immagine di riferimento",
"globalReferenceImage_withCount_one": "$t(controlLayers.globalReferenceImage)",
"globalReferenceImage_withCount_many": "Immagini di riferimento Globali",
"globalReferenceImage_withCount_other": "Immagini di riferimento Globali",
"controlMode": {
"balanced": "Bilanciato (consigliato)",
"controlMode": "Modalità di controllo",
@@ -2149,7 +2085,6 @@
"negativePrompt": "Prompt Negativo",
"prompt": "Prompt Positivo",
"beginEndStepPercentShort": "Inizio/Fine %",
"stagingOnCanvas": "Genera immagini nella",
"ipAdapterMethod": {
"full": "Stile e Composizione",
"style": "Stile (semplice)",
@@ -2298,8 +2233,6 @@
"value": "Valore (HSV)"
}
},
"controlLayers_withCount_hidden": "Livelli di controllo ({{count}} nascosti)",
"regionalGuidance_withCount_hidden": "Guida regionale ({{count}} nascosti)",
"fill": {
"grid": "Griglia",
"crosshatch": "Tratteggio incrociato",
@@ -2308,28 +2241,18 @@
"solid": "Solido",
"vertical": "Verticale",
"horizontal": "Orizzontale",
"diagonal": "Diagonale"
"diagonal": "Diagonale",
"bgFillColor": "Colore di sfondo",
"fgFillColor": "Colore di primo piano"
},
"rasterLayers_withCount_hidden": "Livelli raster ({{count}} nascosti)",
"inpaintMasks_withCount_hidden": "Maschere Inpaint ({{count}} nascoste)",
"regionalGuidance_withCount_visible": "Guide regionali ({{count}})",
"locked": "Bloccato",
"hidingType": "Nascondere {{type}}",
"logDebugInfo": "Registro Info Debug",
"inpaintMasks_withCount_visible": "Maschere Inpaint ({{count}})",
"layer_one": "Livello",
"layer_many": "Livelli",
"layer_other": "Livelli",
"disableTransparencyEffect": "Disabilita l'effetto trasparenza",
"controlLayers_withCount_visible": "Livelli di controllo ({{count}})",
"transparency": "Trasparenza",
"newCanvasSessionDesc": "Questo cancellerà la tela e tutte le impostazioni, eccetto la selezione del modello. Le generazioni saranno effettuate sulla tela.",
"rasterLayers_withCount_visible": "Livelli raster ({{count}})",
"globalReferenceImages_withCount_visible": "Immagini di riferimento Globali ({{count}})",
"globalReferenceImages_withCount_hidden": "Immagini di riferimento globali ({{count}} nascoste)",
"layer_withCount_one": "Livello ({{count}})",
"layer_withCount_many": "Livelli ({{count}})",
"layer_withCount_other": "Livelli ({{count}})",
"unlocked": "Sbloccato",
"enableTransparencyEffect": "Abilita l'effetto trasparenza",
"replaceLayer": "Sostituisci livello",
@@ -2337,9 +2260,6 @@
"pullBboxIntoReferenceImage": "Carica l'immagine delimitata nel riquadro",
"showProgressOnCanvas": "Mostra i progressi sulla Tela",
"weight": "Peso",
"newGallerySession": "Nuova sessione Galleria",
"newGallerySessionDesc": "Questo cancellerà la tela e tutte le impostazioni, eccetto la selezione del modello. Le generazioni saranno inviate alla galleria.",
"newCanvasSession": "Nuova sessione Tela",
"deleteSelected": "Elimina selezione",
"settings": {
"isolatedStagingPreview": "Anteprima di generazione isolata",
@@ -2413,7 +2333,6 @@
"copyBboxToClipboard": "Copia il riquadro di delimitazione negli appunti",
"newResizedControlLayer": "Nuovo livello di controllo ridimensionato"
},
"newImg2ImgCanvasFromImage": "Nuova Immagine da immagine",
"copyRasterLayerTo": "Copia $t(controlLayers.rasterLayer) in",
"copyControlLayerTo": "Copia $t(controlLayers.controlLayer) in",
"copyInpaintMaskTo": "Copia $t(controlLayers.inpaintMask) in",
@@ -2445,7 +2364,6 @@
"convertRegionalGuidanceTo": "Converti $t(controlLayers.regionalGuidance) in",
"newControlLayer": "Nuovo $t(controlLayers.controlLayer)",
"newInpaintMask": "Nuova $t(controlLayers.inpaintMask)",
"replaceCurrent": "Sostituisci corrente",
"mergeDown": "Unire in basso",
"mergingLayers": "Unione dei livelli",
"controlLayerEmptyState": "<UploadButton>Carica un'immagine</UploadButton>, trascina un'immagine dalla galleria su questo livello, <PullBboxButton>trascina il riquadro di delimitazione in questo livello</PullBboxButton> oppure disegna sulla tela per iniziare.",
@@ -2459,7 +2377,6 @@
"newSession": "Nuova sessione",
"resetCanvasLayers": "Ripristina livelli Tela",
"referenceImageRegional": "Immagine di riferimento (regionale)",
"referenceImageGlobal": "Immagine di riferimento (globale)",
"warnings": {
"controlAdapterNoModelSelected": "nessun modello selezionato per il livello di controllo",
"controlAdapterNoControl": "nessun controllo selezionato/disegnato",
@@ -2485,7 +2402,6 @@
"pasteToBbox": "Riquadro di delimitazione",
"pasteToCanvas": "Tela",
"pasteToCanvasDesc": "Nuovo livello (nella Tela)",
"pastedTo": "Incollato su {{destination}}",
"regionCopiedToClipboard": "{{region}} Copiato negli appunti",
"errors": {
"unableToFindImage": "Impossibile trovare l'immagine",
@@ -2508,10 +2424,7 @@
"showNonRasterLayers": "Mostra livelli non raster (Shift+H)",
"hideNonRasterLayers": "Nascondi livelli non raster (Shift+H)",
"referenceImageEmptyStateWithCanvasOptions": "<UploadButton>Carica un'immagine</UploadButton>, trascina un'immagine dalla galleria su questa immagine di riferimento o <PullBboxButton>trascina il riquadro di delimitazione in questa immagine di riferimento</PullBboxButton> per iniziare.",
"uploadOrDragAnImage": "Trascina un'immagine dalla galleria o <UploadButton>carica un'immagine</UploadButton>.",
"autoSwitch": {
"switchOnStart": "All'inizio",
"switchOnFinish": "Alla fine",
"off": "Spento"
},
"invertMask": "Inverti maschera",
@@ -2530,7 +2443,8 @@
"upscaling": "Amplia",
"upscalingTab": "$t(ui.tabs.upscaling) $t(common.tab)",
"gallery": "Galleria",
"generate": "Genera"
"generate": "Genera",
"video": "Video"
},
"launchpad": {
"workflowsTitle": "Approfondisci i flussi di lavoro.",
@@ -2608,13 +2522,19 @@
"generate": {
"canvasCalloutTitle": "Vuoi avere più controllo, modificare e affinare le tue immagini?",
"canvasCalloutLink": "Per ulteriori funzionalità, vai su Tela."
},
"videoTitle": "Genera video da prompt testuale.",
"addStartingFrame": {
"title": "Aggiungi un fotogramma iniziale",
"description": "Aggiungi un'immagine per controllare il primo fotogramma del tuo video."
}
},
"panels": {
"launchpad": "Rampa di lancio",
"workflowEditor": "Editor del flusso di lavoro",
"imageViewer": "Visualizzatore immagini",
"canvas": "Tela"
"imageViewer": "Visualizzatore",
"canvas": "Tela",
"video": "Video"
}
},
"upscaling": {
@@ -2703,10 +2623,9 @@
"whatsNewInInvoke": "Novità in Invoke",
"readReleaseNotes": "Leggi le note di rilascio",
"watchRecentReleaseVideos": "Guarda i video su questa versione",
"watchUiUpdatesOverview": "Guarda le novità dell'interfaccia",
"items": [
"Vari QoL: attiva/disattiva la visibilità del Riquadro di delimitazione, evidenzia i nodi con errori, evita di aggiungere più volte i campi dei nodi al modulo Generatore, i metadati CLIP Skip ora richiamabili",
"Utilizzo ridotto di VRAM per immagini di riferimento Kontext multiple e codifica VAE"
"Tela: Color Picker non campiona l'alfa, il riquadro di delimitazione rispetta il blocco delle proporzioni quando si ridimensiona il pulsante Mescola per i campi numerici nel generatore di flusso di lavoro, nasconde i cursori delle dimensioni dei pixel quando si utilizza un modello che non li supporta",
"Flussi di lavoro: aggiunto un pulsante Mescola ai campi di input numerici"
]
},
"system": {
@@ -2755,5 +2674,8 @@
"clear": "Cancella la cache del modello",
"clearSucceeded": "Cache del modello cancellata",
"clearFailed": "Problema durante la cancellazione della cache del modello"
},
"lora": {
"weight": "Peso"
}
}

View File

@@ -27,7 +27,6 @@
"openInNewTab": "新しいタブで開く",
"controlNet": "コントロールネット",
"linear": "リニア",
"imageFailedToLoad": "画像が読み込めません",
"modelManager": "モデルマネージャー",
"learnMore": "もっと学ぶ",
"random": "ランダム",
@@ -56,7 +55,6 @@
"details": "詳細",
"inpaint": "inpaint",
"delete": "削除",
"nextPage": "次のページ",
"copy": "コピー",
"error": "エラー",
"file": "ファイル",
@@ -64,13 +62,10 @@
"input": "インプット",
"format": "形式",
"installed": "インストール済み",
"localSystem": "ローカルシステム",
"outputs": "アウトプット",
"prevPage": "前のページ",
"unknownError": "未知のエラー",
"orderBy": "並び順:",
"enabled": "有効",
"notInstalled": "未 $t(common.installed)",
"positivePrompt": "ポジティブプロンプト",
"negativePrompt": "ネガティブプロンプト",
"selected": "選択済み",
@@ -96,7 +91,6 @@
"close": "閉じる",
"warnings": "警告",
"dontShowMeThese": "次回から表示しない",
"goTo": "移動",
"generating": "生成中",
"loadingModel": "モデルをロード中",
"layout": "レイアウト",
@@ -107,7 +101,6 @@
"min": "最小",
"max": "最大",
"values": "値",
"resetToDefaults": "デフォルトに戻す",
"row": "行",
"column": "列",
"board": "ボード",
@@ -131,7 +124,6 @@
"gallery": {
"galleryImageSize": "画像のサイズ",
"gallerySettings": "ギャラリーの設定",
"noImagesInGallery": "表示する画像がありません",
"autoSwitchNewImages": "新しい画像に自動切替",
"copy": "コピー",
"image": "画像",
@@ -145,7 +137,6 @@
"deleteImage_other": "画像 {{count}} 枚を削除",
"deleteImagePermanent": "削除された画像は復元できません。",
"download": "ダウンロード",
"unableToLoad": "ギャラリーをロードできません",
"bulkDownloadRequested": "ダウンロード準備中",
"bulkDownloadRequestedDesc": "ダウンロードの準備中です。しばらくお待ちください。",
"bulkDownloadRequestFailed": "ダウンロード準備中に問題が発生",
@@ -160,7 +151,6 @@
"compareImage": "比較画像",
"openInViewer": "ビューアで開く",
"selectForCompare": "比較対象として選択",
"selectAnImageToCompare": "比較する画像を選択",
"slider": "スライダー",
"sideBySide": "横並び",
"hover": "ホバー",
@@ -172,8 +162,6 @@
"compareHelp4": "<Kbd>[Z</Kbd>]または<Kbd>[Esc</Kbd>]を押して終了します。",
"compareHelp2": "<Kbd>M</Kbd> キーを押して比較モードを切り替えます。",
"move": "移動",
"openViewer": "ビューアを開く",
"closeViewer": "ビューアを閉じる",
"exitSearch": "画像検索を終了",
"oldestFirst": "最古から",
"showStarredImagesFirst": "スター付き画像を最初に",
@@ -182,7 +170,6 @@
"searchImages": "メタデータで検索",
"gallery": "ギャラリー",
"newestFirst": "最新から",
"jump": "ジャンプ",
"go": "進む",
"sortDirection": "並び替え順",
"displayBoardSearch": "ボード検索",
@@ -325,10 +312,6 @@
"desc": "リスト内の前のレイヤーを選択します。",
"title": "前のレイヤー"
},
"setFillToWhite": {
"title": "ツール色を白に設定",
"desc": "現在のツールの色を白色に設定します。"
},
"selectViewTool": {
"title": "表示ツール",
"desc": "表示ツールを選択します。"
@@ -609,7 +592,6 @@
"scanResults": "結果をスキャン",
"scanPlaceholder": "ローカルフォルダへのパス",
"typePhraseHere": "ここにフレーズを入力",
"ipAdapters": "IPアダプター",
"modelImageUpdated": "モデル画像アップデート",
"installAll": "全てインストール",
"installRepo": "リポジトリをインストール",
@@ -651,7 +633,6 @@
"spandrelImageToImage": "Image to Image(スパンドレル)",
"starterBundles": "スターターバンドル",
"starterModels": "スターターモデル",
"starterModelsInModelManager": "スターターモデルがモデルマネージャーで見つかりました",
"modelImageDeleteFailed": "モデル画像の削除失敗",
"urlForbidden": "このモデルにアクセスできません",
"urlForbiddenErrorMessage": "このモデルを配布しているサイトからリクエスト権限が必要かもしれません.",
@@ -660,12 +641,10 @@
"inplaceInstall": "定位置にインストール",
"fileSize": "ファイルサイズ",
"modelPickerFallbackNoModelsInstalled2": "<LinkComponent>モデルマネージャー</LinkComponent> にアクセスしてモデルをインストールしてください.",
"filterModels": "フィルターモデル",
"modelPickerFallbackNoModelsInstalled": "モデルがインストールされていません.",
"manageModels": "モデル管理",
"hfTokenReset": "ハギングフェイストークンリセット",
"relatedModels": "関連のあるモデル",
"showOnlyRelatedModels": "関連している",
"installedModelsCount": "{{total}} モデルのうち {{installed}} 個がインストールされています。",
"allNModelsInstalled": "{{count}} 個のモデルがすべてインストールされています",
"nToInstall": "{{count}}個をインストールする",
@@ -682,12 +661,8 @@
"scanFolderDescription": "ローカルフォルダをスキャンしてモデルを自動的に検出し、インストールします。",
"recommendedModels": "推奨モデル",
"exploreStarter": "または、利用可能なすべてのスターターモデルを参照してください",
"quickStart": "クイックスタートバンドル",
"bundleDescription": "各バンドルには各モデルファミリーの必須モデルと、開始するための厳選されたベースモデルが含まれています。",
"browseAll": "または、利用可能なすべてのモデルを参照してください。",
"stableDiffusion15": "Stable Diffusion1.5",
"sdxl": "SDXL",
"fluxDev": "FLUX.1 dev"
"sdxl": "SDXL"
}
},
"parameters": {
@@ -703,12 +678,10 @@
"scaleBeforeProcessing": "処理前のスケール",
"scaledWidth": "幅のスケール",
"scaledHeight": "高さのスケール",
"downloadImage": "画像をダウンロード",
"usePrompt": "プロンプトを使用",
"useSeed": "シード値を使用",
"useAll": "すべてを使用",
"info": "情報",
"showOptionsPanel": "サイドパネルを表示 (O or T)",
"iterations": "生成回数",
"general": "基本設定",
"setToOptimalSize": "サイズをモデルに最適化",
@@ -722,7 +695,6 @@
"collectionNumberLTExclusiveMin": "{{value}} <= {{exclusiveMinimum}} (最小値を除く)",
"missingInputForField": "入力の欠落",
"noModelSelected": "モデルが選択されていません",
"emptyBatches": "空のバッチ",
"collectionStringTooLong": "長すぎます,最大{{maxLength}}",
"batchNodeCollectionSizeMismatchNoGroupId": "バッチグループのコレクションサイズが合いません",
"invoke": "呼び出す",
@@ -734,7 +706,6 @@
"missingNodeTemplate": "ノードテンプレートの欠落",
"batchNodeNotConnected": "バッチノードが: {{label}}につながっていない",
"collectionNumberLTMin": "{{value}} < {{minimum}} (最小増加)",
"fluxModelIncompatibleScaledBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), スケーリングされたbboxの高さは{{height}}です",
"fluxModelMultipleControlLoRAs": "コントロールLoRAは1度に1つしか使用できません",
"noPrompts": "プロンプトが生成されません",
"noNodesInGraph": "グラフにノードがありません",
@@ -742,7 +713,6 @@
"canvasIsFiltering": "キャンバスがビジー状態(フィルタリング)",
"canvasIsCompositing": "キャンバスがビジー状態(合成)",
"systemDisconnected": "システムが切断されました",
"fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), 拡大縮小されたbboxの幅は{{width}}です",
"canvasIsTransforming": "キャンバスがビジー状態(変換)",
"canvasIsRasterizing": "キャンバスがビジー状態(ラスタライズ)",
"modelIncompatibleBboxHeight": "Bboxの高さは{{height}}ですが,{{model}}は{{multiple}}の倍数が必要です",
@@ -750,12 +720,9 @@
"modelIncompatibleBboxWidth": "Bboxの幅は{{width}}ですが, {{model}}は{{multiple}}の倍数が必要です",
"modelIncompatibleScaledBboxWidth": "bboxの幅は{{width}}ですが,{{model}}は{{multiple}}の倍数が必要です",
"canvasIsSelectingObject": "キャンバスがビジー状態(オブジェクトの選択)",
"fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), bboxの幅は{{width}}です",
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), bboxの高さは{{height}}です",
"noFLUXVAEModelSelected": "FLUX生成にVAEモデルが選択されていません",
"noT5EncoderModelSelected": "FLUX生成にT5エンコーダモデルが選択されていません",
"modelDisabledForTrial": "{{modelName}} を使用した生成はトライアルアカウントではご利用いただけません.アカウント設定にアクセスしてアップグレードしてください。",
"fluxKontextMultipleReferenceImages": "Flux Kontext では一度に 1 つの参照画像しか使用できません",
"promptExpansionPending": "プロンプト拡張が進行中",
"promptExpansionResultPending": "プロンプト拡張結果を受け入れるか破棄してください"
},
@@ -830,8 +797,6 @@
"enableHighlightFocusedRegions": "重点領域を強調表示",
"clearIntermediatesDesc1": "中間物をクリアすると、キャンバスとコントロールネットの状態がリセットされます.",
"showProgressInViewer": "ビューアで進行状況画像を表示する",
"modelDescriptionsDisabled": "ドロップダウンのモデル説明が無効になっています",
"modelDescriptionsDisabledDesc": "ドロップダウンのモデル説明が無効になっています.設定で有効にしてください.",
"clearIntermediatesDisabled": "中間物をクリアするにはキューが空でなければなりません",
"clearIntermediatesDesc2": "中間画像は生成時に生成される副産物であり、ギャラリーに表示される結果画像とは異なります.中間画像を削除するとディスク容量が解放されます.",
"intermediatesClearedFailed": "中間物をクリアする問題",
@@ -862,11 +827,9 @@
"imagesWillBeAddedTo": "アップロードされた画像はボード {{boardName}} のアセットに追加されます.",
"layerCopiedToClipboard": "レイヤーがクリップボードにコピーされました",
"pasteFailed": "貼り付け失敗",
"imageSavingFailed": "画像保存に失敗しました",
"importSuccessful": "インポートが成功しました",
"problemDownloadingImage": "画像をダウンロードできません",
"modelAddedSimple": "モデルがキューに追加されました",
"uploadFailedInvalidUploadDesc_withCount_other": "PNG、JPEG、または WEBP 画像は最大 1 つにする必要があります.",
"outOfMemoryErrorDesc": "現在の生成設定はシステム容量を超えています.設定を調整してもう一度お試しください.",
"parametersSet": "パラメーターが呼び出されました",
"modelImportCanceled": "モデルのインポートがキャンセルされました",
@@ -881,14 +844,11 @@
"linkCopied": "リンクがコピーされました",
"unableToLoadImage": "画像をロードできません",
"unableToLoadImageMetadata": "画像のメタデータをロードできません",
"imageSaved": "画像が保存されました",
"importFailed": "インポートに失敗しました",
"invalidUpload": "無効なアップロードです",
"outOfMemoryError": "メモリ不足エラー",
"parameterSetDesc": "{{parameter}}を呼び出し",
"errorCopied": "エラーがコピーされました",
"sentToCanvas": "キャンバスに送信",
"setControlImage": "コントロール画像としてセット",
"workflowLoaded": "ワークフローがロードされました",
"unableToCopy": "コピーできません",
"unableToCopyDesc": "あなたのブラウザはクリップボードアクセスをサポートしていません.Firefoxユーザーの場合は、以下の手順で修正できる可能性があります. ",
@@ -902,32 +862,23 @@
"parameterNotSetDescWithMessage": "{{parameter}}: {{message}}を呼び出せません",
"problemCopyingLayer": "レイヤーをコピーできません",
"problemSavingLayer": "レイヤー保存ができません",
"setNodeField": "ノードフィールドとしてセット",
"layerSavedToAssets": "レイヤーがアセットに保存されました",
"outOfMemoryErrorDescLocal": "OOM を削減するには、<LinkComponent>低 VRAM ガイド</LinkComponent> に従ってください.",
"parameterNotSet": "パラメーターが呼び出されていません",
"addedToBoard": "{{name}} 個の資産をボードに追加しました",
"addedToUncategorized": "$t(boards.uncategorized)個のアセットがボードに追加されました",
"problemDeletingWorkflow": "ワークフローが削除された問題",
"imageNotLoadedDesc": "画像を見つけられません",
"parameterNotSetDesc": "{{parameter}}を呼び出せません",
"chatGPT4oIncompatibleGenerationMode": "ChatGPT 4oは,テキストから画像への生成と画像から画像への生成のみをサポートしています.インペインティングおよび,アウトペインティングタスクには他のモデルを使用してください.",
"imagenIncompatibleGenerationMode": "Google {{model}} はテキストから画像への変換のみをサポートしています. 画像から画像への変換, インペインティング,アウトペインティングのタスクには他のモデルを使用してください.",
"noRasterLayers": "ラスターレイヤーが見つかりません",
"noRasterLayersDesc": "PSDにエクスポートするには、少なくとも1つのラスターレイヤーを作成します",
"noActiveRasterLayers": "アクティブなラスターレイヤーがありません",
"noActiveRasterLayersDesc": "PSD にエクスポートするには、少なくとも 1 つのラスター レイヤーを有効にします",
"noVisibleRasterLayers": "表示されるラスター レイヤーがありません",
"noVisibleRasterLayersDesc": "PSD にエクスポートするには、少なくとも 1 つのラスター レイヤーを有効にします",
"invalidCanvasDimensions": "キャンバスのサイズが無効です",
"canvasTooLarge": "キャンバスが大きすぎます",
"canvasTooLargeDesc": "キャンバスのサイズがPSDエクスポートの最大許容サイズを超えています。キャンバス全体の幅と高さを小さくしてから、もう一度お試しください。",
"failedToProcessLayers": "レイヤーの処理に失敗しました",
"psdExportSuccess": "PSDエクスポート完了",
"psdExportSuccessDesc": "{{count}} 個のレイヤーを PSD ファイルに正常にエクスポートしました",
"problemExportingPSD": "PSD のエクスポート中に問題が発生しました",
"canvasManagerNotAvailable": "キャンバスマネージャーは利用できません",
"noValidLayerAdapters": "有効なレイヤーアダプタが見つかりません",
"fluxKontextIncompatibleGenerationMode": "Flux Kontext はテキストから画像への変換のみをサポートしています。画像から画像への変換、インペインティング、アウトペインティングのタスクには他のモデルを使用してください。",
"promptGenerationStarted": "プロンプト生成が開始されました",
"uploadAndPromptGenerationFailed": "画像のアップロードとプロンプトの生成に失敗しました",
@@ -959,7 +910,6 @@
"positivePrompt": "ポジティブプロンプト",
"strength": "Image to Image 強度",
"recallParameters": "パラメータを再使用",
"recallParameter": "{{label}} を再使用",
"imageDimensions": "画像サイズ",
"imageDetails": "画像の詳細",
"model": "モデル",
@@ -974,7 +924,6 @@
"cfgRescaleMultiplier": "$t(parameters.cfgRescaleMultiplier)",
"canvasV2Metadata": "キャンバス",
"guidance": "手引き",
"parsingFailed": "解析に失敗しました",
"seamlessXAxis": "シームレスX軸",
"seamlessYAxis": "シームレスY軸",
"parameterSet": "パラメーター {{parameter}} が設定されました",
@@ -1062,14 +1011,12 @@
"models": {
"noMatchingModels": "一致するモデルがありません",
"loading": "読み込み中",
"noMatchingLoRAs": "一致するLoRAがありません",
"noModelsAvailable": "使用可能なモデルがありません",
"selectModel": "モデルを選択してください",
"concepts": "コンセプト",
"addLora": "LoRAを追加",
"lora": "LoRA",
"defaultVAE": "デフォルトVAE",
"noLoRAsInstalled": "インストールされているLoRAはありません",
"noRefinerModelsInstalled": "インストールされているSDXLリファイナーモデルはありません",
"noCompatibleLoRAs": "互換性のあるLoRAはありません"
},
@@ -1079,7 +1026,6 @@
"addNodeToolTip": "ノードを追加 (Shift+A, Space)",
"missingTemplate": "Invalid node: タイプ {{type}} のノード {{node}} にテンプレートがありません(未インストール?)",
"loadWorkflow": "ワークフローを読み込み",
"hideLegendNodes": "フィールドタイプの凡例を非表示",
"float": "浮動小数点",
"integer": "整数",
"nodeTemplate": "ノードテンプレート",
@@ -1123,13 +1069,11 @@
"enum": "Enum",
"arithmeticSequence": "等差数列",
"linearDistribution": "線形分布",
"addLinearView": "ライナービューに追加",
"animatedEdges": "アニメーションエッジ",
"uniformRandomDistribution": "一様ランダム分布",
"noBatchGroup": "グループなし",
"parseString": "文字列の解析",
"generatorImagesFromBoard": "ボードからの画像",
"generatorLoading": "読み込み中",
"missingNode": "呼び出しノードがありません",
"missingSourceOrTargetNode": "ソースまたはターゲットノードがありません",
"missingSourceOrTargetHandle": "ソースまたはターゲットハンドルがありません",
@@ -1150,7 +1094,6 @@
"missingInvocationTemplate": "呼び出しテンプレートがありません",
"nodePack": "ノードパック",
"targetNodeFieldDoesNotExist": "無効なエッジ:ターゲット/インプットフィールド{{node}}.{{field}} が存在しません",
"mismatchedVersion": "無効なノード:ノード {{node}} のタイプ {{type}} はバージョンとミスマッチしています (アップデートを試されますか?)",
"dynamicPromptsCombinatorial": "ダイナミックプロンプト(組み合わせ)",
"cannotMixAndMatchCollectionItemTypes": "コレクション・アイテムの種類を組み合わせることはできません",
"missingFieldTemplate": "フィールドテンプレートがありません",
@@ -1160,7 +1103,6 @@
"collectionOrScalarFieldType": "{{name}} (単数またはコレクション)",
"unableToUpdateNode": "ノードアップロード失敗:ノード {{node}} のタイプ {{type}} (削除か再生成が必要かもしれません)",
"deletedInvalidEdge": "無効なエッジを削除しました{{source}} -> {{target}}",
"noFieldsLinearview": "線形ビューに追加されたフィールドがありません",
"collectionFieldType": "{{name}} (コレクション)",
"colorCodeEdgesHelp": "接続されたフィールドによるカラーコードエッジ",
"showEdgeLabelsHelp": "エッジのラベルを表示,接続されているノードを示す",
@@ -1175,7 +1117,6 @@
"loadWorkflowDesc2": "現在のワークフローは保存されていない変更があります.",
"clearWorkflowDesc": "このワークフローをクリアして新しいワークフローにしますか?",
"updateNode": "ノードをアップデート",
"versionUnknown": " バージョン不明",
"graph": "グラフ",
"workflowContact": "お問い合わせ",
"outputFieldTypeParseError": "出力フィールド {{node}}.{{field}} の型を解析できません({{message}})",
@@ -1194,36 +1135,28 @@
"unableToExtractSchemaNameFromRef": "参照からスキーマ名を抽出できません",
"unableToUpdateNodes_other": "{{count}} 個のノードをアップデートできません",
"workflowSettings": "ワークフローエディター設定",
"generateValues": "値を生成",
"floatRangeGenerator": "浮動小数点レンジ生成器",
"integerRangeGenerator": "整数レンジ生成器",
"specialDesc": "この呼び出しは,アプリ内で特別な処理を行います.例えば,バッチードは1つのワークフローから複数のグラフをキューに入れるために使用されます.",
"modelAccessError": "モデル {{key}}が見つからないので,デフォルトにリセットします",
"betaDesc": "この呼び出しはベータ版です.安定するまでは,アプリのアップデートの際に変更される可能性があります.この呼び出しは長期的にサポートする予定です.",
"internalDesc": "この呼び出しはInvokeによって内部的に使用されます.アプリの更新時に変更される可能性があり,いつでも削除される可能性があります.",
"noFieldsViewMode": "このワークフローには表示する選択フィールドがありません.値を設定するためにはワークフロー全体を表示します.",
"clearWorkflow": "ワークフローをクリア",
"removeLinearView": "線形ビューから削除",
"snapToGrid": "グリッドにスナップ",
"showMinimapnodes": "ミニマップを表示",
"reorderLinearView": "線形ビューの並び替え",
"description": "説明",
"notesDescription": "ワークフローに関するメモを追加する",
"newWorkflowDesc2": "現在のワークフローに保存されていない変更があります.",
"unknownField": "不明なフィールド",
"unexpectedField_withName": "予期しないフィールド\"{{name}}\"",
"loadingTemplates": "読み込み中 {{name}}",
"validateConnectionsHelp": "無効な接続が行われたり,無効なグラフが呼び出されたりしないようにします",
"validateConnections": "接続とグラフを確認する",
"saveToGallery": "ギャラリーに保存",
"newWorkflowDesc": "新しいワークフローを作りますか?",
"unknownFieldType": "$t(nodes.unknownField)型: {{type}}",
"unsupportedArrayItemType": "サポートされていない配列項目型です \"{{type}}\"",
"unableToLoadWorkflow": "ワークフローが読み込めません",
"unableToValidateWorkflow": "ワークフローを確認できません",
"unknownErrorValidatingWorkflow": "ワークフローの確認で不明なエラーが発生",
"clearWorkflowDesc2": "現在のワークフローは保存されていない変更があります.",
"showLegendNodes": "フィールドタイプの凡例を表示",
"unsupportedMismatchedUnion": "CollectionOrScalar型とベース型{{firstType}}および{{secondType}}が不一致です",
"updateApp": "アプリケーションをアップデート",
"noGraph": "グラフなし",
@@ -1241,10 +1174,8 @@
"workflowDescription": "短い説明",
"workflowValidation": "ワークフロー検証エラー",
"noOutputRecorded": "記録されたアウトプットがありません",
"unknownTemplate": "不明なテンプレート",
"nodeOpacity": "ノードの不透明度",
"unableToParseFieldType": "フィールドタイプを解析できません",
"unknownInput": "不明な入力: {{name}}"
"unableToParseFieldType": "フィールドタイプを解析できません"
},
"boards": {
"autoAddBoard": "自動追加するボード",
@@ -1268,7 +1199,6 @@
"deleteBoardOnly": "ボードのみ削除",
"deletedBoardsCannotbeRestored": "削除したボードと画像は復元できません。「ボードのみ削除」を選択すると、画像は未分類の状態になります。",
"movingImagesToBoard_other": "{{count}} の画像をボードに移動:",
"hideBoards": "ボードを隠す",
"assetsWithCount_other": "{{count}} のアセット",
"addPrivateBoard": "プライベートボードを追加",
"addSharedBoard": "共有ボードを追加",
@@ -1283,10 +1213,8 @@
"selectedForAutoAdd": "自動追加に選択済み",
"deletedPrivateBoardsCannotbeRestored": "削除されたボードと画像は復元できません。「ボードのみ削除」を選択すると、画像は作成者に対して非公開の未分類状態になります。",
"noBoards": "{{boardType}} ボードがありません",
"viewBoards": "ボードを表示",
"uncategorizedImages": "分類されていない画像",
"deleteAllUncategorizedImages": "分類されていないすべての画像を削除",
"deletedImagesCannotBeRestored": "削除した画像は復元できません."
"deleteAllUncategorizedImages": "分類されていないすべての画像を削除"
},
"invocationCache": {
"invocationCache": "呼び出しキャッシュ",
@@ -1758,9 +1686,7 @@
"strength": "高解像修復の強度",
"enabled": "高解像修復が有効"
},
"enableHrf": "高解像修復を有効",
"hrf": "高解像修復",
"upscaleMethod": "アップスケール手法"
"hrf": "高解像修復"
},
"prompt": {
"addPromptTrigger": "プロンプトトリガーを追加",
@@ -1770,10 +1696,7 @@
"expandCurrentPrompt": "現在のプロンプトを展開",
"uploadImageForPromptGeneration": "プロンプト生成用の画像をアップロードする",
"expandingPrompt": "プロンプトを展開しています...",
"resultTitle": "プロンプト拡張完了",
"resultSubtitle": "拡張プロンプトの処理方法を選択します:",
"replace": "交換する",
"insert": "挿入する",
"discard": "破棄する"
},
"ui": {
@@ -1839,11 +1762,9 @@
}
},
"controlLayers": {
"globalReferenceImage_withCount_other": "全域参照画像",
"regionalReferenceImage": "領域参照画像",
"saveLayerToAssets": "レイヤーをアセットに保存",
"global": "全域",
"inpaintMasks_withCount_hidden": "インペイントマスク ({{count}} hidden)",
"opacity": "透明度",
"canvasContextMenu": {
"newRegionalGuidance": "新規領域ガイダンス",
@@ -1895,7 +1816,6 @@
"duplicate": "複製",
"addLayer": "レイヤーを追加",
"rasterLayer": "ラスターレイヤー",
"inpaintMasks_withCount_visible": "({{count}}) インペイントマスク",
"regional": "領域",
"rectangle": "矩形",
"moveBackward": "背面へ移動",
@@ -2097,7 +2017,6 @@
"autoNegative": "オートネガティブ",
"enableAutoNegative": "オートネガティブを有効にする",
"disableAutoNegative": "オートネガティブを無効にする",
"deletePrompt": "プロンプトを削除",
"deleteReferenceImage": "参照画像を削除",
"showHUD": "HUDを表示",
"maskFill": "マスク塗りつぶし",
@@ -2109,41 +2028,22 @@
"addControlLayer": "$t(controlLayers.controlLayer)を追加します",
"addInpaintMask": "$t(controlLayers.inpaintMask)を追加します",
"addRegionalGuidance": "$t(controlLayers.regionalGuidance)を追加します",
"addGlobalReferenceImage": "$t(controlLayers.globalReferenceImage)を追加します",
"addDenoiseLimit": "$t(controlLayers.denoiseLimit)を追加します",
"controlLayer": "コントロールレイヤー",
"inpaintMask": "インペイントマスク",
"referenceImageRegional": "参考画像(地域別)",
"referenceImageGlobal": "参考画像(グローバル)",
"asRasterLayer": "$t(controlLayers.rasterLayer) として",
"asRasterLayerResize": "$t(controlLayers.rasterLayer) として (リサイズ)",
"asControlLayer": "$t(controlLayers.controlLayer) として",
"asControlLayerResize": "$t(controlLayers.controlLayer) として (リサイズ)",
"referenceImage": "参照画像",
"sendingToCanvas": "キャンバスに生成をのせる",
"sendingToGallery": "生成をギャラリーに送る",
"sendToGallery": "ギャラリーに送る",
"sendToGalleryDesc": "Invokeを押すとユニークな画像が生成され、ギャラリーに保存されます。",
"sendToCanvas": "キャンバスに送る",
"newLayerFromImage": "画像から新規レイヤー",
"newCanvasFromImage": "画像から新規キャンバス",
"newImg2ImgCanvasFromImage": "画像からの新規 Img2Img",
"copyToClipboard": "クリップボードにコピー",
"sendToCanvasDesc": "Invokeを押すと、進行中の作品がキャンバス上にステージされます。",
"viewProgressInViewer": "<Btn>画像ビューア</Btn>で進行状況と出力を表示します。",
"viewProgressOnCanvas": "<Btn>キャンバス</Btn> で進行状況とステージ出力を表示します。",
"rasterLayer_withCount_other": "ラスターレイヤー",
"controlLayer_withCount_other": "コントロールレイヤー",
"regionalGuidance_withCount_hidden": "地域ガイダンス({{count}} 件非表示)",
"controlLayers_withCount_hidden": "コントロールレイヤー({{count}} 個非表示)",
"rasterLayers_withCount_hidden": "ラスター レイヤー ({{count}} 個非表示)",
"globalReferenceImages_withCount_hidden": "グローバル参照画像({{count}} 枚非表示)",
"regionalGuidance_withCount_visible": "地域ガイダンス ({{count}})",
"controlLayers_withCount_visible": "コントロールレイヤー ({{count}})",
"rasterLayers_withCount_visible": "ラスターレイヤー({{count}}",
"globalReferenceImages_withCount_visible": "グローバル参照画像 ({{count}})",
"layer_other": "レイヤー",
"layer_withCount_other": "レイヤー ({{count}})",
"convertRasterLayerTo": "$t(controlLayers.rasterLayer) を変換する",
"convertControlLayerTo": "$t(controlLayers.controlLayer) を変換する",
"convertRegionalGuidanceTo": "$t(controlLayers.regionalGuidance) を変換する",
@@ -2161,7 +2061,6 @@
"pasteToBboxDesc": "新しいレイヤーBbox内",
"pasteToCanvas": "キャンバス",
"pasteToCanvasDesc": "新しいレイヤー(キャンバス内)",
"pastedTo": "{{destination}} に貼り付けました",
"transparency": "透明性",
"enableTransparencyEffect": "透明効果を有効にする",
"disableTransparencyEffect": "透明効果を無効にする",
@@ -2174,7 +2073,6 @@
"locked": "ロックされています",
"unlocked": "ロック解除",
"deleteSelected": "選択項目を削除",
"stagingOnCanvas": "ステージング画像",
"replaceLayer": "レイヤーの置き換え",
"pullBboxIntoLayer": "Bboxをレイヤーに引き込む",
"pullBboxIntoReferenceImage": "Bboxを参照画像に取り込む",
@@ -2182,17 +2080,11 @@
"useImage": "画像を使う",
"negativePrompt": "ネガティブプロンプト",
"beginEndStepPercentShort": "開始/終了 %",
"newGallerySession": "新しいギャラリーセッション",
"newGallerySessionDesc": "これにより、キャンバスとモデル選択以外のすべての設定がクリアされます。生成した画像はギャラリーに送信されます。",
"newCanvasSession": "新規キャンバスセッション",
"newCanvasSessionDesc": "これにより、キャンバスとモデル選択以外のすべての設定がクリアされます。生成はキャンバス上でステージングされます。",
"resetCanvasLayers": "キャンバスレイヤーをリセット",
"resetGenerationSettings": "生成設定をリセット",
"replaceCurrent": "現在のものを置き換える",
"controlLayerEmptyState": "<UploadButton>画像をアップロード</UploadButton>、<GalleryButton>ギャラリー</GalleryButton>からこのレイヤーに画像をドラッグ、<PullBboxButton>境界ボックスをこのレイヤーにプル</PullBboxButton>、またはキャンバスに描画して開始します。",
"referenceImageEmptyStateWithCanvasOptions": "開始するには、<UploadButton>画像をアップロード</UploadButton>するか、<GalleryButton>ギャラリー</GalleryButton>からこの参照画像に画像をドラッグするか、<PullBboxButton>境界ボックスをこの参照画像にプル</PullBboxButton>します。",
"referenceImageEmptyState": "開始するには、<UploadButton>画像をアップロード</UploadButton>するか、<GalleryButton>ギャラリー</GalleryButton>からこの参照画像に画像をドラッグします。",
"uploadOrDragAnImage": "ギャラリーから画像をドラッグするか、<UploadButton>画像をアップロード</UploadButton>します。",
"imageNoise": "画像ノイズ",
"denoiseLimit": "ノイズ除去制限",
"warnings": {
@@ -2361,12 +2253,8 @@
"loading": "ロード中...",
"steps": "ステップ",
"refiner": "Refiner",
"negStylePrompt": "ネガティブスタイルプロンプト",
"noModelsAvailable": "利用できるモデルがありません",
"posStylePrompt": "ポジティブスタイルプロンプト",
"cfgScale": "CFGスケール",
"concatPromptStyle": "リンキングプロンプトとスタイル",
"freePromptStyle": "手動スタイルプロンプト",
"posAestheticScore": "ポジティブ美的スコア",
"refinerSteps": "リファイナーステップ",
"refinerStart": "リファイナースタート",
@@ -2384,8 +2272,6 @@
"name": "名前",
"descending": "降順",
"searchPlaceholder": "名前、説明、タグで検索",
"projectWorkflows": "プロジェクトワークフロー",
"searchWorkflows": "ワークフローを検索",
"updated": "アップデート",
"published": "公表",
"builder": {
@@ -2411,10 +2297,8 @@
"addToForm": "フォームに追加",
"headingPlaceholder": "空の見出し",
"nodeFieldTooltip": "ノード フィールドを追加するには、ワークフロー エディターのフィールドにある小さなプラス記号ボタンをクリックするか、フィールド名をフォームにドラッグします。",
"workflowBuilderAlphaWarning": "ワークフロービルダーは現在アルファ版です。安定版リリースまでに互換性に影響する変更が発生する可能性があります。",
"component": "コンポーネント",
"textPlaceholder": "空のテキスト",
"emptyRootPlaceholderViewMode": "このワークフローのフォームの作成を開始するには、[編集] をクリックします。",
"addOption": "オプションを追加",
"singleLine": "単線",
"numberInput": "数値入力",
@@ -2465,20 +2349,15 @@
"convertGraph": "グラフを変換",
"downloadWorkflow": "ファイルに保存",
"saveWorkflow": "ワークフローを保存",
"userWorkflows": "ユーザーワークフロー",
"yourWorkflows": "あなたのワークフロー",
"edit": "編集",
"workflowLibrary": "ワークフローライブラリ",
"workflowSaved": "ワークフローが保存されました",
"clearWorkflowSearchFilter": "ワークフロー検索フィルタをクリア",
"workflowCleared": "ワークフローが作成されました",
"autoLayout": "オートレイアウト",
"view": "ビュー",
"saveChanges": "変更を保存",
"noDescription": "説明なし",
"recommended": "あなたへのおすすめ",
"noRecentWorkflows": "最近のワークフローがありません",
"problemLoading": "ワークフローのローディングに関する問題",
"newWorkflowCreated": "新しいワークフローが作成されました",
"noWorkflows": "ワークフローがありません",
"copyShareLink": "共有リンクをコピー",
@@ -2486,21 +2365,16 @@
"workflowThumbnail": "ワークフローサムネイル",
"loadWorkflow": "$t(common.load) ワークフロー",
"shared": "共有",
"openWorkflow": "ワークフローを開く",
"emptyStringPlaceholder": "<空の文字列>",
"browseWorkflows": "ワークフローを閲覧する",
"saveWorkflowAs": "ワークフローとして保存",
"private": "プライベート",
"deselectAll": "すべて選択解除",
"delete": "削除",
"openLibrary": "ライブラリを開く",
"loadMore": "もっと読み込む",
"saveWorkflowToProject": "ワークフローをプロジェクトに保存",
"created": "作成されました",
"workflowEditorMenu": "ワークフローエディターメニュー",
"defaultWorkflows": "デフォルトワークフロー",
"allLoaded": "すべてのワークフローが読み込まれました",
"filterByTags": "タグでフィルター",
"recentlyOpened": "最近開いた",
"opened": "オープン",
"deleteWorkflow": "ワークフローを削除",
@@ -2546,7 +2420,6 @@
"perIterationDesc": "それぞれのいてレーションに別のシードを使う"
},
"showDynamicPrompts": "ダイナミックプロンプトを表示する",
"promptsToGenerate": "生成するプロンプト",
"dynamicPrompts": "ダイナミックプロンプト",
"loading": "ダイナミックプロンプトを生成...",
"maxPrompts": "最大プロンプト"
@@ -2572,8 +2445,7 @@
"キャンバス: SDXL のアスペクト比がスマートになり、スクロールによるズームが改善されました。"
],
"readReleaseNotes": "リリースノートを読む",
"watchRecentReleaseVideos": "最近のリリースビデオを見る",
"watchUiUpdatesOverview": "Watch UI アップデートの概要"
"watchRecentReleaseVideos": "最近のリリースビデオを見る"
},
"supportVideos": {
"supportVideos": "サポートビデオ",

View File

@@ -27,7 +27,6 @@
"save": "저장",
"created": "생성됨",
"error": "에러",
"prevPage": "이전 페이지",
"ipAdapter": "IP 어댑터",
"installed": "설치됨",
"accept": "수락",
@@ -42,7 +41,6 @@
"outputs": "결과물",
"unknownError": "알려지지 않은 에러",
"linear": "선형",
"imageFailedToLoad": "이미지를 로드할 수 없음",
"direction": "방향",
"data": "데이터",
"somethingWentWrong": "뭔가 잘못됐어요",
@@ -52,7 +50,6 @@
"orderBy": "정렬 기준",
"copyError": "$t(gallery.copy) 에러",
"learnMore": "더 알아보기",
"nextPage": "다음 페이지",
"saveAs": "다른 이름으로 저장",
"loading": "불러오는 중",
"random": "랜덤",
@@ -60,18 +57,15 @@
"postprocessing": "후처리",
"advanced": "고급",
"input": "입력",
"details": "세부사항",
"notInstalled": "설치되지 않음"
"details": "세부사항"
},
"gallery": {
"galleryImageSize": "이미지 크기",
"gallerySettings": "갤러리 설정",
"deleteSelection": "선택 항목 삭제",
"featuresWillReset": "이 이미지를 삭제하면 해당 기능이 즉시 재설정됩니다.",
"noImagesInGallery": "보여줄 이미지가 없음",
"autoSwitchNewImages": "새로운 이미지로 자동 전환",
"loading": "불러오는 중",
"unableToLoad": "갤러리를 로드할 수 없음",
"image": "이미지",
"drop": "드랍",
"downloadSelection": "선택 항목 다운로드",
@@ -151,8 +145,6 @@
"loadWorkflow": "Workflow 불러오기",
"noOutputRecorded": "기록된 출력 없음",
"colorCodeEdgesHelp": "연결된 필드에 따른 색상 코드 선",
"hideLegendNodes": "필드 유형 범례 숨기기",
"addLinearView": "Linear View에 추가",
"float": "실수",
"targetNodeFieldDoesNotExist": "잘못된 모서리: 대상/입력 필드 {{node}}. {{field}}이(가) 없습니다",
"animatedEdges": "애니메이션 모서리",
@@ -160,7 +152,6 @@
"nodeTemplate": "노드 템플릿",
"nodeOpacity": "노드 불투명도",
"sourceNodeDoesNotExist": "잘못된 모서리: 소스/출력 노드 {{node}}이(가) 없습니다",
"noFieldsLinearview": "Linear View에 추가된 필드 없음",
"nodeSearch": "노드 검색",
"inputMayOnlyHaveOneConnection": "입력에 하나의 연결만 있을 수 있습니다",
"notes": "메모",
@@ -195,7 +186,6 @@
"notesDescription": "Workflow에 대한 메모 추가",
"colorCodeEdges": "색상-코드 선",
"targetNodeDoesNotExist": "잘못된 모서리: 대상/입력 노드 {{node}}이(가) 없습니다",
"mismatchedVersion": "잘못된 노드: {{type}} 유형의 {{node}} 노드에 일치하지 않는 버전이 있습니다(업데이트 해보시겠습니까?)",
"addNodeToolTip": "노드 추가(Shift+A, Space)",
"collectionOrScalarFieldType": "{{name}} 컬렉션|Scalar",
"nodeVersion": "노드 버전",
@@ -296,8 +286,6 @@
"cacheSize": "캐시 크기"
},
"hrf": {
"enableHrf": "이용 가능한 고해상도 고정",
"upscaleMethod": "업스케일 방법",
"metadata": {
"strength": "고해상도 고정 강도",
"enabled": "고해상도 고정 사용",
@@ -308,12 +296,10 @@
"models": {
"noMatchingModels": "일치하는 모델 없음",
"loading": "로딩중",
"noMatchingLoRAs": "일치하는 LoRA 없음",
"noModelsAvailable": "사용 가능한 모델이 없음",
"addLora": "LoRA 추가",
"selectModel": "모델 선택",
"noRefinerModelsInstalled": "SDXL Refiner 모델이 설치되지 않음",
"noLoRAsInstalled": "설치된 LoRA 없음"
"noRefinerModelsInstalled": "SDXL Refiner 모델이 설치되지 않음"
},
"boards": {
"autoAddBoard": "자동 추가 Board",

View File

@@ -30,12 +30,10 @@
"ipAdapter": "IP-adapter",
"auto": "Autom.",
"controlNet": "ControlNet",
"imageFailedToLoad": "Kan afbeelding niet laden",
"learnMore": "Meer informatie",
"advanced": "Uitgebreid",
"file": "Bestand",
"installed": "Geïnstalleerd",
"notInstalled": "Niet $t(common.installed)",
"simple": "Eenvoudig",
"somethingWentWrong": "Er ging iets mis",
"add": "Voeg toe",
@@ -43,14 +41,12 @@
"details": "Details",
"outputs": "Uitvoeren",
"save": "Bewaar",
"nextPage": "Volgende pagina",
"blue": "Blauw",
"alpha": "Alfa",
"red": "Rood",
"editor": "Editor",
"folder": "Map",
"format": "structuur",
"goTo": "Ga naar",
"template": "Sjabloon",
"input": "Invoer",
"safetensors": "Safetensors",
@@ -62,7 +58,6 @@
"negativePrompt": "Negatieve prompt",
"selected": "Geselecteerd",
"orderBy": "Sorteer op",
"prevPage": "Vorige pagina",
"beta": "Bèta",
"copyError": "$t(gallery.copy) Fout",
"toResolve": "Op te lossen",
@@ -79,21 +74,18 @@
"delete": "Verwijder",
"direction": "Richting",
"error": "Fout",
"localSystem": "Lokaal systeem",
"unknownError": "Onbekende fout"
},
"gallery": {
"galleryImageSize": "Afbeeldingsgrootte",
"gallerySettings": "Instellingen galerij",
"autoSwitchNewImages": "Wissel autom. naar nieuwe afbeeldingen",
"noImagesInGallery": "Geen afbeeldingen om te tonen",
"deleteImage_one": "Verwijder afbeelding",
"deleteImage_other": "",
"deleteImagePermanent": "Verwijderde afbeeldingen kunnen niet worden hersteld.",
"autoAssignBoardOnClick": "Ken automatisch bord toe bij klikken",
"featuresWillReset": "Als je deze afbeelding verwijdert, dan worden deze functies onmiddellijk teruggezet.",
"loading": "Bezig met laden",
"unableToLoad": "Kan galerij niet laden",
"downloadSelection": "Download selectie",
"currentlyInUse": "Deze afbeelding is momenteel in gebruik door de volgende functies:",
"copy": "Kopieer",
@@ -199,12 +191,10 @@
"scaledHeight": "Geschaalde H",
"infillMethod": "Infill-methode",
"tileSize": "Grootte tegel",
"downloadImage": "Download afbeelding",
"usePrompt": "Hergebruik invoertekst",
"useSeed": "Hergebruik seed",
"useAll": "Hergebruik alles",
"info": "Info",
"showOptionsPanel": "Toon deelscherm Opties (O of T)",
"symmetry": "Symmetrie",
"cancel": {
"cancel": "Annuleer"
@@ -293,15 +283,12 @@
"baseModelChangedCleared_one": "Basismodel is gewijzigd: {{count}} niet-compatibel submodel weggehaald of uitgeschakeld",
"baseModelChangedCleared_other": "Basismodel is gewijzigd: {{count}} niet-compatibele submodellen weggehaald of uitgeschakeld",
"loadedWithWarnings": "Werkstroom geladen met waarschuwingen",
"setControlImage": "Ingesteld als controle-afbeelding",
"setNodeField": "Ingesteld als knooppuntveld",
"imageUploaded": "Afbeelding geüpload",
"addedToBoard": "Toegevoegd aan bord",
"workflowLoaded": "Werkstroom geladen",
"modelAddedSimple": "Model toegevoegd aan wachtrij",
"imageUploadFailed": "Fout bij uploaden afbeelding",
"workflowDeleted": "Werkstroom verwijderd",
"invalidUpload": "Ongeldige upload",
"problemRetrievingWorkflow": "Fout bij ophalen van werkstroom",
"parameters": "Parameters",
"modelImportCanceled": "Importeren model geannuleerd",
@@ -325,17 +312,14 @@
"zoomOutNodes": "Uitzoomen",
"fitViewportNodes": "Aanpassen aan beeld",
"hideMinimapnodes": "Minimap verbergen",
"showLegendNodes": "Typelegende veld tonen",
"zoomInNodes": "Inzoomen",
"showMinimapnodes": "Minimap tonen",
"hideLegendNodes": "Typelegende veld verbergen",
"reloadNodeTemplates": "Herlaad knooppuntsjablonen",
"loadWorkflow": "Laad werkstroom",
"downloadWorkflow": "Download JSON van werkstroom",
"scheduler": "Planner",
"missingTemplate": "Ongeldig knooppunt: knooppunt {{node}} van het soort {{type}} heeft een ontbrekend sjabloon (niet geïnstalleerd?)",
"workflowDescription": "Korte beschrijving",
"versionUnknown": " Versie onbekend",
"noNodeSelected": "Geen knooppunt gekozen",
"addNode": "Voeg knooppunt toe",
"unableToValidateWorkflow": "Kan werkstroom niet valideren",
@@ -349,9 +333,7 @@
"integer": "Geheel getal",
"nodeTemplate": "Sjabloon knooppunt",
"nodeOpacity": "Dekking knooppunt",
"unableToLoadWorkflow": "Fout bij laden werkstroom",
"snapToGrid": "Lijn uit op raster",
"noFieldsLinearview": "Geen velden toegevoegd aan lineaire weergave",
"nodeSearch": "Zoek naar knooppunten",
"updateNode": "Werk knooppunt bij",
"version": "Versie",
@@ -370,9 +352,7 @@
"edge": "Rand",
"animatedEdgesHelp": "Animeer gekozen randen en randen verbonden met de gekozen knooppunten",
"cannotDuplicateConnection": "Kan geen dubbele verbindingen maken",
"unknownTemplate": "Onbekend sjabloon",
"noWorkflow": "Geen werkstroom",
"removeLinearView": "Verwijder uit lineaire weergave",
"workflowTags": "Labels",
"fullyContainNodesHelp": "Knooppunten moeten zich volledig binnen het keuzevak bevinden om te worden gekozen",
"workflowValidation": "Validatiefout werkstroom",
@@ -397,14 +377,11 @@
"unknownField": "Onbekend veld",
"colorCodeEdges": "Kleurgecodeerde randen",
"unknownNode": "Onbekend knooppunt",
"mismatchedVersion": "Ongeldig knooppunt: knooppunt {{node}} van het soort {{type}} heeft een niet-overeenkomende versie (probeer het bij te werken?)",
"addNodeToolTip": "Voeg knooppunt toe (Shift+A, spatie)",
"loadingNodes": "Bezig met laden van knooppunten...",
"snapToGridHelp": "Lijn knooppunten uit op raster bij verplaatsing",
"workflowSettings": "Instellingen werkstroomeditor",
"addLinearView": "Voeg toe aan lineaire weergave",
"nodePack": "Knooppuntpakket",
"unknownInput": "Onbekende invoer: {{name}}",
"sourceNodeFieldDoesNotExist": "Ongeldige rand: bron-/uitvoerveld {{node}}.{{field}} bestaat niet",
"collectionFieldType": "Verzameling {{name}}",
"deletedInvalidEdge": "Ongeldige hoek {{source}} -> {{target}} verwijderd",
@@ -419,7 +396,6 @@
"sourceNodeDoesNotExist": "Ongeldige rand: bron-/uitvoerknooppunt {{node}} bestaat niet",
"unsupportedArrayItemType": "niet-ondersteunde soort van het array-onderdeel \"{{type}}\"",
"targetNodeFieldDoesNotExist": "Ongeldige rand: doel-/invoerveld {{node}}.{{field}} bestaat niet",
"reorderLinearView": "Herorden lineaire weergave",
"newWorkflowDesc": "Een nieuwe werkstroom aanmaken?",
"collectionOrScalarFieldType": "Verzameling|scalair {{name}}",
"newWorkflow": "Nieuwe werkstroom",
@@ -734,27 +710,21 @@
"refinerStart": "Startwaarde verfijning",
"scheduler": "Planner",
"cfgScale": "CFG-schaal",
"negStylePrompt": "Negatieve-stijlprompt",
"noModelsAvailable": "Geen modellen beschikbaar",
"refiner": "Verfijning",
"negAestheticScore": "Negatieve esthetische score",
"denoisingStrength": "Sterkte ontruising",
"refinermodel": "Verfijningsmodel",
"posAestheticScore": "Positieve esthetische score",
"concatPromptStyle": "Koppelen van prompt en stijl",
"loading": "Bezig met laden...",
"steps": "Stappen",
"posStylePrompt": "Positieve-stijlprompt",
"freePromptStyle": "Handmatige stijlprompt",
"refinerSteps": "Aantal stappen verfijner"
},
"models": {
"noMatchingModels": "Geen overeenkomend modellen",
"loading": "bezig met laden",
"noMatchingLoRAs": "Geen overeenkomende LoRA's",
"noModelsAvailable": "Geen modellen beschikbaar",
"selectModel": "Kies een model",
"noLoRAsInstalled": "Geen LoRA's geïnstalleerd",
"noRefinerModelsInstalled": "Geen SDXL-verfijningsmodellen geïnstalleerd",
"defaultVAE": "Standaard-VAE",
"lora": "LoRA",
@@ -822,14 +792,12 @@
}
},
"hrf": {
"upscaleMethod": "Opschaalmethode",
"metadata": {
"strength": "Sterkte oplossing voor hoge resolutie",
"method": "Methode oplossing voor hoge resolutie",
"enabled": "Oplossing voor hoge resolutie ingeschakeld"
},
"hrf": "Oplossing voor hoge resolutie",
"enableHrf": "Schakel oplossing in voor hoge resolutie"
"hrf": "Oplossing voor hoge resolutie"
},
"prompt": {
"addPromptTrigger": "Voeg prompttrigger toe",

View File

@@ -41,11 +41,9 @@
"somethingWentWrong": "Coś poszło nie tak",
"green": "Zielony",
"red": "Czerwony",
"imageFailedToLoad": "Nie można załadować obrazu",
"saveAs": "Zapisz jako",
"outputs": "Wyjścia",
"data": "Dane",
"localSystem": "System Lokalny",
"t2iAdapter": "Adapter T2I",
"selected": "Zaznaczone",
"warnings": "Ostrzeżenia",
@@ -64,12 +62,10 @@
"openInViewer": "Otwórz podgląd",
"safetensors": "Bezpieczniki",
"ok": "Ok",
"goTo": "Idź do",
"loadingImage": "wczytywanie zdjęcia",
"input": "Wejście",
"view": "Podgląd",
"learnMore": "Dowiedz się więcej",
"notInstalled": "Nie $t(common.installed)",
"loadingModel": "Wczytywanie modelu",
"postprocessing": "Przetwarzanie końcowe",
"random": "Losowo",
@@ -83,10 +79,8 @@
"delete": "Usuń",
"template": "Szablon",
"txt2img": "Tekst na obraz",
"prevPage": "Poprzednia strona",
"file": "Plik",
"toResolve": "Do rozwiązania",
"nextPage": "Następna strona",
"unknownError": "Nieznany błąd",
"placeholderSelectAModel": "Wybierz model",
"new": "Nowy",
@@ -99,7 +93,6 @@
"galleryImageSize": "Rozmiar obrazów",
"gallerySettings": "Ustawienia galerii",
"autoSwitchNewImages": "Przełączaj na nowe obrazy",
"noImagesInGallery": "Brak obrazów w galerii",
"gallery": "Galeria",
"alwaysShowImageSizeBadge": "Zawsze pokazuj odznakę wielkości obrazu",
"assetsTab": "Pliki, które wrzuciłeś do użytku w twoich projektach.",
@@ -128,12 +121,10 @@
"scaledHeight": "Sk. do wys.",
"infillMethod": "Metoda wypełniania",
"tileSize": "Rozmiar kafelka",
"downloadImage": "Pobierz obraz",
"usePrompt": "Skopiuj sugestie",
"useSeed": "Skopiuj inicjator",
"useAll": "Skopiuj wszystko",
"info": "Informacje",
"showOptionsPanel": "Pokaż panel ustawień"
"info": "Informacje"
},
"settings": {
"models": "Modele",
@@ -186,8 +177,6 @@
"selectedForAutoAdd": "Wybrany do automatycznego dodania",
"deleteBoard": "Usuń tablicę",
"clearSearch": "Usuń historię",
"hideBoards": "Ukryj tablice",
"viewBoards": "Zobacz tablice",
"addSharedBoard": "Dodaj udostępnioną tablicę",
"boards": "Tablice",
"addPrivateBoard": "Dodaj prywatną tablicę",
@@ -233,8 +222,7 @@
"strength": "Moc poprawki wysokiej rozdzielczości",
"method": "Metoda High Resolution Fix"
},
"hrf": "Poprawka \"Wysoka rozdzielczość\"",
"enableHrf": "Włącz poprawkę wysokiej rozdzielczości"
"hrf": "Poprawka \"Wysoka rozdzielczość\""
},
"queue": {
"cancelTooltip": "Anuluj aktualną pozycję",

View File

@@ -17,8 +17,7 @@
"gallery": {
"galleryImageSize": "Tamanho da Imagem",
"gallerySettings": "Configurações de Galeria",
"autoSwitchNewImages": "Trocar para Novas Imagens Automaticamente",
"noImagesInGallery": "Sem Imagens na Galeria"
"autoSwitchNewImages": "Trocar para Novas Imagens Automaticamente"
},
"modelManager": {
"modelManager": "Gerente de Modelo",
@@ -74,12 +73,10 @@
"scaledHeight": "A Escalada",
"infillMethod": "Método de Preenchimento",
"tileSize": "Tamanho do Ladrilho",
"downloadImage": "Baixar Imagem",
"usePrompt": "Usar Prompt",
"useSeed": "Usar Seed",
"useAll": "Usar Todos",
"info": "Informações",
"showOptionsPanel": "Mostrar Painel de Opções",
"symmetry": "Simetria",
"copyImage": "Copiar imagem",
"denoisingStrength": "A força de remoção de ruído",

View File

@@ -17,7 +17,6 @@
"gallery": {
"gallerySettings": "Configurações de Galeria",
"autoSwitchNewImages": "Trocar para Novas Imagens Automaticamente",
"noImagesInGallery": "Sem Imagens na Galeria",
"galleryImageSize": "Tamanho da Imagem"
},
"modelManager": {
@@ -69,7 +68,6 @@
"tileSize": "Tamanho do Ladrilho",
"symmetry": "Simetria",
"usePrompt": "Usar Prompt",
"showOptionsPanel": "Mostrar Painel de Opções",
"strength": "Força",
"upscaling": "Redimensionando",
"scaleBeforeProcessing": "Escala Antes do Processamento",
@@ -81,7 +79,6 @@
"scaledHeight": "A Escalada",
"infillMethod": "Método de Preenchimento",
"copyImage": "Copiar imagem",
"downloadImage": "Descarregar Imagem",
"useSeed": "Usar Seed",
"useAll": "Usar Todos",
"info": "Informações"

View File

@@ -38,7 +38,6 @@
"save": "Сохранить",
"created": "Создано",
"error": "Ошибка",
"prevPage": "Предыдущая страница",
"simple": "Простой",
"ipAdapter": "IP Adapter",
"installed": "Установлено",
@@ -49,7 +48,6 @@
"template": "Шаблон",
"outputs": "результаты",
"unknownError": "Неизвестная ошибка",
"imageFailedToLoad": "Невозможно загрузить изображение",
"direction": "Направление",
"data": "Данные",
"somethingWentWrong": "Что-то пошло не так",
@@ -58,11 +56,9 @@
"orderBy": "Сортировать по",
"copyError": "Ошибка $t(gallery.copy)",
"learnMore": "Узнать больше",
"nextPage": "Следущая страница",
"saveAs": "Сохранить как",
"input": "Вход",
"details": "Детали",
"notInstalled": "Нет $t(common.installed)",
"or": "или",
"aboutHeading": "Владей своей творческой силой",
"red": "Красный",
@@ -71,7 +67,6 @@
"alpha": "Альфа",
"toResolve": "Чтоб решить",
"copy": "Копировать",
"localSystem": "Локальная система",
"aboutDesc": "Используя Invoke для работы? Проверьте это:",
"add": "Добавить",
"beta": "Бета",
@@ -79,7 +74,6 @@
"positivePrompt": "Позитивный запрос",
"negativePrompt": "Негативный запрос",
"editor": "Редактор",
"goTo": "Перейти к",
"tab": "Вкладка",
"enabled": "Включено",
"disabled": "Отключено",
@@ -101,7 +95,6 @@
"galleryImageSize": "Размер изображений",
"gallerySettings": "Настройка галереи",
"autoSwitchNewImages": "Автоматически выбирать новые",
"noImagesInGallery": "Изображений нет",
"deleteImagePermanent": "Удаленные изображения невозможно восстановить.",
"deleteImage_one": "Удалить изображение",
"deleteImage_few": "Удалить {{count}} изображения",
@@ -110,7 +103,6 @@
"deleteSelection": "Удалить выделенное",
"featuresWillReset": "Если вы удалите это изображение, эти функции будут немедленно сброшены.",
"loading": "Загрузка",
"unableToLoad": "Невозможно загрузить галерею",
"image": "изображение",
"drop": "перебросить",
"downloadSelection": "Скачать выделенное",
@@ -136,7 +128,6 @@
"compareHelp4": "Нажмите <Kbd>Z</Kbd> или <Kbd>Esc</Kbd> для выхода.",
"compareImage": "Сравнить изображение",
"viewerImage": "Изображение просмотрщика",
"selectAnImageToCompare": "Выберите изображение для сравнения",
"slider": "Слайдер",
"sideBySide": "Бок о бок",
"compareHelp1": "Удерживайте <Kbd>Alt</Kbd> при нажатии на изображение в галерее или при помощи клавиш со стрелками, чтобы изменить сравниваемое изображение.",
@@ -154,11 +145,8 @@
"exitBoardSearch": "Выйти из поиска досок",
"go": "Перейти",
"exitSearch": "Выйти из поиска изображений",
"jump": "Пыгнуть",
"move": "Двигать",
"gallery": "Галерея",
"openViewer": "Открыть просмотрщик",
"closeViewer": "Закрыть просмотрщик",
"imagesTab": "Изображения, созданные и сохраненные в Invoke.",
"assetsTab": "Файлы, которые вы загрузили для использования в своих проектах.",
"boardsSettings": "Настройки доски",
@@ -285,10 +273,6 @@
"title": "Next Layer",
"desc": "Select the next layer in the list."
},
"setFillToWhite": {
"title": "Set Color to White",
"desc": "Set the current tool color to white."
},
"applyFilter": {
"title": "Apply Filter",
"desc": "Apply the pending filter to the selected layer."
@@ -578,8 +562,6 @@
"noModelsInstalled": "Нет установленных моделей",
"noModelsInstalledDesc1": "Установите модели с помощью",
"noMatchingModels": "Нет подходящих моделей",
"ipAdapters": "IP адаптеры",
"starterModelsInModelManager": "Стартовые модели можно найти в Менеджере моделей",
"learnMoreAboutSupportedModels": "Подробнее о поддерживаемых моделях",
"t5Encoder": "T5 энкодер",
"spandrelImageToImage": "Image to Image (Spandrel)",
@@ -616,12 +598,10 @@
"scaledHeight": "Масштаб В",
"infillMethod": "Способ заполнения",
"tileSize": "Размер области",
"downloadImage": "Скачать",
"usePrompt": "Использовать запрос",
"useSeed": "Использовать сид",
"useAll": "Использовать все",
"info": "Метаданные",
"showOptionsPanel": "Показать панель настроек",
"cancel": {
"cancel": "Отмена"
},
@@ -647,10 +627,6 @@
"missingFieldTemplate": "Отсутствует шаблон поля",
"addingImagesTo": "Добавление изображений в",
"invoke": "Создать",
"fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), ширина рамки {{width}}",
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), высота рамки {{height}}",
"fluxModelIncompatibleScaledBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), масштабированная высота рамки {{height}}",
"fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16) масштабированная ширина рамки {{width}}",
"noFLUXVAEModelSelected": "Для генерации FLUX не выбрана модель VAE",
"noT5EncoderModelSelected": "Для генерации FLUX не выбрана модель T5 энкодера",
"canvasIsFiltering": "Холст фильтруется",
@@ -736,9 +712,6 @@
"baseModelChangedCleared_few": "Очищено или отключено {{count}} несовместимых подмодели",
"baseModelChangedCleared_many": "Очищено или отключено {{count}} несовместимых подмоделей",
"loadedWithWarnings": "Рабочий процесс загружен с предупреждениями",
"setControlImage": "Установить как контрольное изображение",
"setNodeField": "Установить как поле узла",
"invalidUpload": "Неверная загрузка",
"imageUploaded": "Изображение загружено",
"addedToBoard": "Добавлено в активы доски {{name}}",
"workflowLoaded": "Рабочий процесс загружен",
@@ -767,21 +740,14 @@
"sentToCanvas": "Отправить на холст",
"unableToLoadImage": "Невозможно загрузить изображение",
"unableToLoadImageMetadata": "Невозможно загрузить метаданные изображения",
"imageSaved": "Изображение сохранено",
"stylePresetLoaded": "Предустановка стиля загружена",
"imageNotLoadedDesc": "Не удалось найти изображение",
"imageSavingFailed": "Не удалось сохранить изображение",
"problemCopyingLayer": "Не удалось скопировать слой",
"unableToLoadStylePreset": "Невозможно загрузить предустановку стиля",
"layerCopiedToClipboard": "Слой скопирован в буфер обмена",
"sentToUpscale": "Отправить на увеличение",
"layerSavedToAssets": "Слой сохранен в активах",
"linkCopied": "Ссылка скопирована",
"addedToUncategorized": "Добавлено в активы доски $t(boards.uncategorized)",
"imagesWillBeAddedTo": "Загруженные изображения будут добавлены в активы доски {{boardName}}.",
"uploadFailedInvalidUploadDesc_withCount_one": "Должно быть не более {{count}} изображения в формате PNG или JPEG.",
"uploadFailedInvalidUploadDesc_withCount_few": "Должно быть не более {{count}} изображений в формате PNG или JPEG.",
"uploadFailedInvalidUploadDesc_withCount_many": "Должно быть не более {{count}} изображений в формате PNG или JPEG."
"imagesWillBeAddedTo": "Загруженные изображения будут добавлены в активы доски {{boardName}}."
},
"accessibility": {
"uploadImage": "Загрузить изображение",
@@ -803,15 +769,12 @@
"zoomInNodes": "Увеличьте масштаб",
"zoomOutNodes": "Уменьшите масштаб",
"fitViewportNodes": "Уместить вид",
"showLegendNodes": "Показать тип поля",
"hideMinimapnodes": "Скрыть миникарту",
"hideLegendNodes": "Скрыть тип поля",
"showMinimapnodes": "Показать миникарту",
"loadWorkflow": "Загрузить рабочий процесс",
"reloadNodeTemplates": "Перезагрузить шаблоны узлов",
"downloadWorkflow": "Скачать JSON рабочего процесса",
"addNode": "Добавить узел",
"addLinearView": "Добавить в линейный вид",
"animatedEdges": "Анимированные ребра",
"animatedEdgesHelp": "Анимация выбранных ребер и ребер, соединенных с выбранными узлами",
"boolean": "Логические значения",
@@ -823,7 +786,6 @@
"workflowDescription": "Краткое описание",
"inputFieldTypeParseError": "Невозможно разобрать тип поля ввода {{node}}.{{field}} ({{message}})",
"unsupportedAnyOfLength": "слишком много элементов объединения ({{count}})",
"versionUnknown": " Версия неизвестна",
"unsupportedArrayItemType": "неподдерживаемый тип элемента массива \"{{type}}\"",
"noNodeSelected": "Узел не выбран",
"unableToValidateWorkflow": "Невозможно проверить рабочий процесс",
@@ -841,10 +803,8 @@
"nodeTemplate": "Шаблон узла",
"nodeOpacity": "Непрозрачность узла",
"sourceNodeDoesNotExist": "Недопустимое ребро: исходный/выходной узел {{node}} не существует",
"unableToLoadWorkflow": "Невозможно загрузить рабочий процесс",
"unableToExtractEnumOptions": "невозможно извлечь параметры перечисления",
"snapToGrid": "Привязка к сетке",
"noFieldsLinearview": "Нет полей, добавленных в линейный вид",
"unableToParseFieldType": "невозможно проанализировать тип поля",
"nodeSearch": "Поиск узлов",
"updateNode": "Обновить узел",
@@ -865,9 +825,7 @@
"edge": "Край",
"sourceNodeFieldDoesNotExist": "Неверный край: поле источника/вывода {{node}}.{{field}} не существует",
"cannotDuplicateConnection": "Невозможно создать дубликаты соединений",
"unknownTemplate": "Неизвестный шаблон",
"noWorkflow": "Нет рабочего процесса",
"removeLinearView": "Удалить из линейного вида",
"workflowTags": "Теги",
"fullyContainNodesHelp": "Чтобы узлы были выбраны, они должны полностью находиться в поле выбора",
"unableToGetWorkflowVersion": "Не удалось получить версию схемы рабочего процесса",
@@ -900,7 +858,6 @@
"colorCodeEdges": "Ребра с цветовой кодировкой",
"unknownNode": "Неизвестный узел",
"targetNodeDoesNotExist": "Недопустимое ребро: целевой/входной узел {{node}} не существует",
"mismatchedVersion": "Недопустимый узел: узел {{node}} типа {{type}} имеет несоответствующую версию (попробовать обновить?)",
"unknownFieldType": "$t(nodes.unknownField) тип: {{type}}",
"collectionOrScalarFieldType": "{{name}} (Один или коллекция)",
"betaDesc": "Этот вызов находится в бета-версии. Пока он не станет стабильным, в нем могут происходить изменения при обновлении приложений. Мы планируем поддерживать этот вызов в течение длительного времени.",
@@ -909,14 +866,12 @@
"snapToGridHelp": "Привязка узлов к сетке при перемещении",
"workflowSettings": "Настройки редактора рабочих процессов",
"deletedInvalidEdge": "Удалено недопустимое ребро {{source}} -> {{target}}",
"unknownInput": "Неизвестный вход: {{name}}",
"newWorkflow": "Новый рабочий процесс",
"newWorkflowDesc": "Создать новый рабочий процесс?",
"clearWorkflow": "Очистить рабочий процесс",
"newWorkflowDesc2": "Текущий рабочий процесс имеет несохраненные изменения.",
"clearWorkflowDesc": "Очистить этот рабочий процесс и создать новый?",
"clearWorkflowDesc2": "Текущий рабочий процесс имеет несохраненные измерения.",
"reorderLinearView": "Изменить порядок линейного просмотра",
"viewMode": "Использовать в линейном представлении",
"editMode": "Открыть в редакторе узлов",
"resetToDefaultValue": "Сбросить к стандартному значкнию",
@@ -978,8 +933,6 @@
"addPrivateBoard": "Добавить личную доску",
"private": "Личные доски",
"shared": "Общие доски",
"hideBoards": "Скрыть доски",
"viewBoards": "Просмотреть доски",
"noBoards": "Нет досок {{boardType}}",
"deletedPrivateBoardsCannotbeRestored": "Удаленные доски не могут быть восстановлены. Выбор «Удалить только доску» переведет изображения в приватное состояние без категории для создателя изображения.",
"updateBoardError": "Ошибка обновления доски"
@@ -1408,8 +1361,6 @@
"noRecallParameters": "Параметры для вызова не найдены",
"cfgRescaleMultiplier": "$t(parameters.cfgRescaleMultiplier)",
"parameterSet": "Параметр {{parameter}} установлен",
"parsingFailed": "Не удалось выполнить синтаксический анализ",
"recallParameter": "Отозвать {{label}}",
"allPrompts": "Все запросы",
"imageDimensions": "Размеры изображения",
"canvasV2Metadata": "Холст",
@@ -1495,18 +1446,14 @@
"refinerStart": "Запуск доработчика",
"scheduler": "Планировщик",
"cfgScale": "Шкала точности (CFG)",
"negStylePrompt": "Негативный запрос стиля",
"noModelsAvailable": "Нет доступных моделей",
"refiner": "Доработчик",
"negAestheticScore": "Отрицательная эстетическая оценка",
"denoisingStrength": "Шумоподавление",
"refinermodel": "Дорабатывающая модель",
"posAestheticScore": "Положительная эстетическая оценка",
"concatPromptStyle": "Связывание запроса и стиля",
"loading": "Загрузка...",
"steps": "Шаги",
"posStylePrompt": "Запрос стиля",
"freePromptStyle": "Ручной запрос стиля",
"refinerSteps": "Шаги доработчика"
},
"invocationCache": {
@@ -1531,20 +1478,15 @@
"workflowEditorMenu": "Меню редактора рабочего процесса",
"workflowName": "Имя рабочего процесса",
"saveWorkflow": "Сохранить рабочий процесс",
"openWorkflow": "Открытый рабочий процесс",
"clearWorkflowSearchFilter": "Очистить фильтр поиска рабочих процессов",
"workflowLibrary": "Библиотека",
"downloadWorkflow": "Сохранить в файл",
"workflowSaved": "Рабочий процесс сохранен",
"unnamedWorkflow": "Безымянный рабочий процесс",
"savingWorkflow": "Сохранение рабочего процесса...",
"problemLoading": "Проблема с загрузкой рабочих процессов",
"loading": "Загрузка рабочих процессов",
"searchWorkflows": "Поиск рабочих процессов",
"problemSavingWorkflow": "Проблема с сохранением рабочего процесса",
"deleteWorkflow": "Удалить рабочий процесс",
"workflows": "Рабочие процессы",
"noDescription": "Без описания",
"uploadWorkflow": "Загрузить из файла",
"newWorkflowCreated": "Создан новый рабочий процесс",
"saveWorkflowToProject": "Сохранить рабочий процесс в проект",
@@ -1560,9 +1502,6 @@
"convertGraph": "Конвертировать график",
"loadFromGraph": "Загрузка рабочего процесса из графика",
"autoLayout": "Автоматическое расположение",
"userWorkflows": "Пользовательские рабочие процессы",
"projectWorkflows": "Рабочие процессы проекта",
"defaultWorkflows": "Стандартные рабочие процессы",
"deleteWorkflow2": "Вы уверены, что хотите удалить этот рабочий процесс? Это нельзя отменить.",
"chooseWorkflowFromLibrary": "Выбрать рабочий процесс из библиотеки",
"edit": "Редактировать",
@@ -1572,8 +1511,6 @@
"delete": "Удалить"
},
"hrf": {
"enableHrf": "Включить исправление высокого разрешения",
"upscaleMethod": "Метод увеличения",
"metadata": {
"strength": "Сила исправления высокого разрешения",
"enabled": "Исправление высокого разрешения включено",
@@ -1584,12 +1521,10 @@
"models": {
"noMatchingModels": "Нет подходящих моделей",
"loading": "загрузка",
"noMatchingLoRAs": "Нет подходящих LoRA",
"noModelsAvailable": "Нет доступных моделей",
"addLora": "Добавить LoRA",
"selectModel": "Выберите модель",
"noRefinerModelsInstalled": "Дорабатывающие модели SDXL не установлены",
"noLoRAsInstalled": "Нет установленных LoRA",
"lora": "LoRA",
"defaultVAE": "Стандартное VAE",
"concepts": "LoRA"
@@ -1624,7 +1559,6 @@
"moveForward": "Переместить вперёд",
"moveBackward": "Переместить назад",
"autoNegative": "Авто негатив",
"deletePrompt": "Удалить запрос",
"rectangle": "Прямоугольник",
"addNegativePrompt": "Добавить $t(controlLayers.negativePrompt)",
"regionalGuidance": "Региональная точность",
@@ -1794,7 +1728,6 @@
},
"addReferenceImage": "Добавить $t(controlLayers.referenceImage)",
"inpaintMask": "Маска перерисовки",
"sendToGalleryDesc": "При нажатии кнопки Invoke создается изображение и сохраняется в вашей галерее.",
"sendToCanvas": "Отправить на холст",
"regionalGuidance_withCount_one": "$t(controlLayers.regionalGuidance)",
"regionalGuidance_withCount_few": "Региональных точности",
@@ -1806,7 +1739,6 @@
"inpaintMask_withCount_one": "$t(controlLayers.inpaintMask)",
"inpaintMask_withCount_few": "Маски перерисовки",
"inpaintMask_withCount_many": "Масок перерисовки",
"globalReferenceImages_withCount_visible": "Глобальные эталонные изображения ({{count}})",
"controlMode": {
"prompt": "Запрос",
"controlMode": "Режим контроля",
@@ -1842,7 +1774,6 @@
"pullBboxIntoReferenceImage": "Поместить рамку в эталонное изображение",
"enableAutoNegative": "Включить авто негатив",
"maskFill": "Заполнение маски",
"viewProgressInViewer": "Просматривайте прогресс и результаты в <Btn>Просмотрщике изображений</Btn>.",
"tool": {
"move": "Двигать",
"bbox": "Ограничительная рамка",
@@ -1853,18 +1784,10 @@
"colorPicker": "Подборщик цветов"
},
"rasterLayer": "Растровый слой",
"sendingToCanvas": "Постановка генераций на холст",
"rasterLayers_withCount_visible": "Растровые слои ({{count}})",
"regionalGuidance_withCount_hidden": "Региональная точность ({{count}} скрыто)",
"enableTransparencyEffect": "Включить эффект прозрачности",
"hidingType": "Скрыть {{type}}",
"addRegionalGuidance": "Добавить $t(controlLayers.regionalGuidance)",
"sendingToGallery": "Отправка генераций в галерею",
"viewProgressOnCanvas": "Просматривайте прогресс и результаты этапов на <Btn>Холсте</Btn>.",
"controlLayers_withCount_hidden": "Контрольные слои ({{count}} скрыто)",
"rasterLayers_withCount_hidden": "Растровые слои ({{count}} скрыто)",
"deleteSelected": "Удалить выбранное",
"stagingOnCanvas": "Постановка изображений на",
"pullBboxIntoLayer": "Поместить рамку в слой",
"locked": "Заблокировано",
"replaceLayer": "Заменить слой",
@@ -1873,16 +1796,10 @@
"addRasterLayer": "Добавить $t(controlLayers.rasterLayer)",
"addControlLayer": "Добавить $t(controlLayers.controlLayer)",
"addInpaintMask": "Добавить $t(controlLayers.inpaintMask)",
"inpaintMasks_withCount_hidden": "Маски перерисовки ({{count}} скрыто)",
"regionalGuidance_withCount_visible": "Региональная точность ({{count}})",
"newGallerySessionDesc": "Это очистит холст и все настройки, кроме выбранной модели. Генерации будут отправлены в галерею.",
"newCanvasSession": "Новая сессия холста",
"newCanvasSessionDesc": "Это очистит холст и все настройки, кроме выбора модели. Генерации будут размещены на холсте.",
"cropLayerToBbox": "Обрезать слой по ограничительной рамке",
"clipToBbox": "Обрезка штрихов в рамке",
"outputOnlyMaskedRegions": "Вывод только маскированных областей",
"duplicate": "Дублировать",
"inpaintMasks_withCount_visible": "Маски перерисовки ({{count}})",
"layer_one": "Слой",
"layer_few": "Слоя",
"layer_many": "Слоев",
@@ -1901,33 +1818,20 @@
},
"disableAutoNegative": "Отключить авто негатив",
"deleteReferenceImage": "Удалить эталонное изображение",
"controlLayers_withCount_visible": "Контрольные слои ({{count}})",
"rasterLayer_withCount_one": "$t(controlLayers.rasterLayer)",
"rasterLayer_withCount_few": "Растровых слоя",
"rasterLayer_withCount_many": "Растровых слоев",
"transparency": "Прозрачность",
"weight": "Вес",
"newGallerySession": "Новая сессия галереи",
"sendToCanvasDesc": "Нажатие кнопки Invoke отображает вашу текущую работу на холсте.",
"globalReferenceImages_withCount_hidden": "Глобальные эталонные изображения ({{count}} скрыто)",
"layer_withCount_one": "Слой ({{count}})",
"layer_withCount_few": "Слои ({{count}})",
"layer_withCount_many": "Слои ({{count}})",
"disableTransparencyEffect": "Отключить эффект прозрачности",
"showingType": "Показать {{type}}",
"dynamicGrid": "Динамическая сетка",
"logDebugInfo": "Писать отладочную информацию",
"unlocked": "Разблокировано",
"showProgressOnCanvas": "Показать прогресс на холсте",
"globalReferenceImage_withCount_one": "$t(controlLayers.globalReferenceImage)",
"globalReferenceImage_withCount_few": "Глобальных эталонных изображения",
"globalReferenceImage_withCount_many": "Глобальных эталонных изображений",
"regionalReferenceImage": "Региональное эталонное изображение",
"globalReferenceImage": "Глобальное эталонное изображение",
"sendToGallery": "Отправить в галерею",
"referenceImage": "Эталонное изображение",
"addGlobalReferenceImage": "Добавить $t(controlLayers.globalReferenceImage)",
"newImg2ImgCanvasFromImage": "Новое img2img из изображения"
"referenceImage": "Эталонное изображение"
},
"ui": {
"tabs": {

View File

@@ -28,7 +28,6 @@
"gallery": {
"galleryImageSize": "Bildstorlek",
"gallerySettings": "Galleriinställningar",
"noImagesInGallery": "Inga bilder i galleriet",
"autoSwitchNewImages": "Ändra automatiskt till nya bilder"
}
}

View File

@@ -36,12 +36,10 @@
"communityLabel": "Topluluk",
"back": "Geri",
"areYouSure": "Emin misiniz?",
"notInstalled": "$t(common.installed) Değil",
"openInNewTab": "Yeni Sekmede Aç",
"aboutHeading": "Yaratıcı Gücünüzün Sahibi Olun",
"load": "Yükle",
"loading": "Yükleniyor",
"localSystem": "Yerel Sistem",
"inpaint": "içboyama",
"modelManager": "Model Yöneticisi",
"orderBy": "Sırala",
@@ -65,11 +63,8 @@
"format": "biçim",
"details": "Ayrıntılar",
"error": "Hata",
"imageFailedToLoad": "Görsel Yüklenemedi",
"safetensors": "Safetensors",
"upload": "Yükle",
"nextPage": "Sonraki Sayfa",
"prevPage": "Önceki Sayfa",
"dontAskMeAgain": "Bir daha sorma",
"delete": "Kaldır",
"direction": "Yön",
@@ -207,12 +202,10 @@
"image": "görsel",
"galleryImageSize": "Görsel Boyutu",
"copy": "Kopyala",
"noImagesInGallery": "Gösterilecek Görsel Yok",
"autoSwitchNewImages": "Yeni Görseli Biter Bitmez Gör",
"currentlyInUse": "Bu görsel şurada kullanımda:",
"deleteImage_one": "Görseli Sil",
"deleteImage_other": "",
"unableToLoad": "Galeri Yüklenemedi",
"downloadSelection": "Seçileni İndir",
"dropOrUpload": "$t(gallery.drop) ya da Yükle",
"dropToUpload": "Yüklemek için $t(gallery.drop)",
@@ -220,13 +213,11 @@
},
"hrf": {
"hrf": "Yüksek Çözünürlük Kürü",
"enableHrf": "Yüksek Çözünürlük Kürünü Aç",
"metadata": {
"enabled": "Yüksek Çözünürlük Kürü Açık",
"strength": "Yüksek Çözünürlük Kürü Etkisi",
"method": "Yüksek Çözünürlük Kürü Yöntemi"
},
"upscaleMethod": "Büyütme Yöntemi"
}
},
"hotkeys": {
"noHotkeysFound": "Kısayol Tuşu Bulanamadı",
@@ -256,7 +247,6 @@
"unknownErrorValidatingWorkflow": "İş akışını doğrulamada bilinmeyen bir sorun",
"unableToGetWorkflowVersion": "İş akışı sürümüne ulaşılamadı",
"newWorkflowDesc2": "Geçerli iş akışında kaydedilmemiş değişiklikler var.",
"unableToLoadWorkflow": "İş Akışı Yüklenemedi",
"cannotConnectInputToInput": "Giriş girişe bağlanamaz",
"zoomInNodes": "Yakınlaştır",
"boolean": "Boole Değeri",
@@ -267,16 +257,12 @@
"cannotDuplicateConnection": "Kopya bağlantılar yaratılamaz"
},
"workflows": {
"searchWorkflows": "İş Akışlarında Ara",
"workflowName": "İş Akışı Adı",
"problemSavingWorkflow": "İş Akışını Kaydetmede Sorun",
"saveWorkflow": "İş Akışını Kaydet",
"uploadWorkflow": "Dosyadan Yükle",
"newWorkflowCreated": "Yeni İş Akışı Yaratıldı",
"problemLoading": "İş Akışlarını Yüklemede Sorun",
"loading": "İş Akışları Yükleniyor",
"noDescription": "Tanımsız",
"clearWorkflowSearchFilter": "İş Akışı Aramasını Resetle",
"workflowEditorMenu": "İş Akışı Düzenleyici Menüsü",
"downloadWorkflow": "İndir",
"saveWorkflowAs": "İş Akışını Farklı Kaydet",
@@ -328,7 +314,6 @@
"noiseThreshold": "Gürültü Eşiği",
"seed": "Tohum",
"imageActions": "Görsel İşlemleri",
"showOptionsPanel": "Yan Paneli Göster (O ya da T)",
"shuffle": "Kar",
"usePrompt": "İstemi Kullan",
"setToOptimalSizeTooSmall": "$t(parameters.setToOptimalSize) (çok küçük olabilir)",
@@ -346,7 +331,6 @@
"perlinNoise": "Perlin Gürültüsü",
"scaledWidth": "Ölçekli En",
"seamlessXAxis": "Dikişsiz Döşeme X Ekseni",
"downloadImage": "Görseli İndir",
"type": "Tür"
},
"modelManager": {
@@ -399,11 +383,9 @@
"defaultVAE": "Varsayılan VAE",
"lora": "LoRA",
"noModelsAvailable": "Model yok",
"noMatchingLoRAs": "Uygun LoRA Yok",
"noMatchingModels": "Uygun Model Yok",
"loading": "yükleniyor",
"selectModel": "Model Seçin",
"noLoRAsInstalled": "LoRA Yok"
"selectModel": "Model Seçin"
},
"settings": {
"generation": "Oluşturma"
@@ -411,7 +393,6 @@
"sdxl": {
"cfgScale": "CFG Ölçeği",
"loading": "Yükleniyor...",
"denoisingStrength": "Arındırma Ölçüsü",
"concatPromptStyle": "İstem ve Stili Bitiştir"
"denoisingStrength": "Arındırma Ölçüsü"
}
}

View File

@@ -22,8 +22,7 @@
"gallery": {
"galleryImageSize": "Розмір зображень",
"gallerySettings": "Налаштування галереї",
"autoSwitchNewImages": "Автоматично вибирати нові",
"noImagesInGallery": "Зображень немає"
"autoSwitchNewImages": "Автоматично вибирати нові"
},
"modelManager": {
"modelManager": "Менеджер моделей",
@@ -80,12 +79,10 @@
"scaledHeight": "Масштаб В",
"infillMethod": "Засіб заповнення",
"tileSize": "Розмір області",
"downloadImage": "Завантажити",
"usePrompt": "Використати запит",
"useSeed": "Використати сід",
"useAll": "Використати все",
"info": "Метадані",
"showOptionsPanel": "Показати панель налаштувань",
"general": "Основне",
"denoisingStrength": "Сила шумоподавлення",
"copyImage": "Копіювати зображення",

View File

@@ -20,8 +20,6 @@
"addBoard": "Thêm Bảng",
"downloadBoard": "Tải Xuống Bảng",
"movingImagesToBoard_other": "Di chuyển {{count}} ảnh vào Bảng:",
"viewBoards": "Xem Bảng",
"hideBoards": "Ẩn Bảng",
"noBoards": "Không Có Bảng Thuộc Loại {{boardType}}",
"noMatching": "Không Có Bảng Tương Ứng",
"searchBoard": "Tìm Bảng...",
@@ -55,7 +53,6 @@
"assetsWithCount_other": "{{count}} tài nguyên",
"uncategorizedImages": "Ảnh Chưa Sắp Xếp",
"deleteAllUncategorizedImages": "Xoá Tất Cả Ảnh Chưa Sắp Xếp",
"deletedImagesCannotBeRestored": "Ảnh đã xoá không thể phục hồi lại.",
"locateInGalery": "Vị Trí Ở Thư Viện Ảnh"
},
"gallery": {
@@ -84,33 +81,27 @@
"galleryImageSize": "Kích Thước Ảnh",
"downloadSelection": "Tải xuống Phần Được Lựa Chọn",
"bulkDownloadRequested": "Chuẩn Bị Tải Xuống",
"unableToLoad": "Không Thể Tải Thư viện Ảnh",
"newestFirst": "Mới Nhất Trước",
"showStarredImagesFirst": "Hiển Thị Ảnh Gắn Sao Trước",
"bulkDownloadRequestedDesc": "Yêu cầu tải xuống đang được chuẩn bị. Vui lòng chờ trong giây lát.",
"starImage": "Gắn Sao Cho Ảnh",
"openViewer": "Mở Trình Xem",
"viewerImage": "Trình Xem Ảnh",
"sideBySide": "Cạnh Nhau",
"alwaysShowImageSizeBadge": "Luôn Hiển Thị Kích Thước Ảnh",
"autoAssignBoardOnClick": "Tự Động Gán Vào Bảng Khi Nhấp Chuột",
"jump": "Nhảy Đến",
"go": "Đi",
"autoSwitchNewImages": "Tự Động Đổi Sang Hình Ảnh Mới",
"featuresWillReset": "Nếu bạn xoá hình ảnh này, những tính năng đó sẽ lập tức được khởi động lại.",
"openInViewer": "Mở Trong Trình Xem",
"searchImages": "Tìm Theo Metadata",
"selectForCompare": "Chọn Để So Sánh",
"closeViewer": "Đóng Trình Xem",
"move": "Di Chuyển",
"displayBoardSearch": "Tìm Kiếm Bảng",
"displaySearch": "Tìm Kiếm Hình Ảnh",
"selectAnImageToCompare": "Chọn Ảnh Để So Sánh",
"slider": "Thanh Trượt",
"gallerySettings": "Cài Đặt Thư Viện Ảnh",
"image": "hình ảnh",
"noImageSelected": "Không Có Ảnh Được Chọn",
"noImagesInGallery": "Không Có Ảnh Để Hiển Thị",
"assetsTab": "Tài liệu bạn đã tải lên để dùng cho dự án của mình.",
"imagesTab": "Ảnh bạn vừa được tạo và lưu trong Invoke.",
"loading": "Đang Tải",
@@ -135,14 +126,12 @@
"clipboard": "Clipboard",
"learnMore": "Tìm Hiểu Thêm",
"openInViewer": "Mở Trong Trình Xem",
"nextPage": "Trang Sau",
"alpha": "Alpha",
"edit": "Sửa",
"nodes": "Workflow",
"format": "Định Dạng",
"delete": "Xoá",
"details": "Chi Tiết",
"imageFailedToLoad": "Không Thể Tải Hình Ảnh",
"img2img": "Hình ảnh sang Hình ảnh",
"upload": "Tải Lên",
"somethingWentWrong": "Có vấn đề phát sinh",
@@ -180,19 +169,15 @@
"on": "Bật",
"checkpoint": "Checkpoint",
"txt2img": "Từ Ngữ Sang Hình Ảnh",
"prevPage": "Trang Trước",
"unknown": "Không Rõ",
"githubLabel": "Github",
"folder": "Thư mục",
"goTo": "Đến",
"hotkeysLabel": "Phím Tắt",
"loadingImage": "Đang Tải Hình ảnh",
"localSystem": "Hệ Thống Máy Chủ",
"input": "Đầu Vào",
"languagePickerLabel": "Ngôn Ngữ",
"openInNewTab": "Mở Trong Tab Mới",
"outpaint": "outpaint",
"notInstalled": "Chưa $t(common.installed)",
"save": "Lưu",
"saveAs": "Lưu Như",
"auto": "Tự Động",
@@ -234,7 +219,6 @@
"end": "Kết Thúc",
"min": "Tối Thiểu",
"max": "Tối Đa",
"resetToDefaults": "Đặt Lại Về Mặc Định",
"seed": "Hạt Giống",
"combinatorial": "Tổ Hợp",
"column": "Cột",
@@ -266,10 +250,7 @@
"expandCurrentPrompt": "Mở Rộng Lệnh Hiện Tại",
"uploadImageForPromptGeneration": "Tải Ảnh Để Tạo Sinh Lệnh",
"expandingPrompt": "Đang mở rộng lệnh...",
"resultTitle": "Mở Rộng Lệnh Hoàn Tất",
"resultSubtitle": "Chọn phương thức mở rộng lệnh:",
"replace": "Thay Thế",
"insert": "Chèn",
"discard": "Huỷ Bỏ"
},
"queue": {
@@ -360,10 +341,6 @@
"desc": "Phóng to canvas lên 800%.",
"title": "Phóng To Vào 800%"
},
"setFillToWhite": {
"title": "Chỉnh Màu Sang Trắng",
"desc": "Chỉnh màu hiện tại sang màu trắng."
},
"transformSelected": {
"title": "Biến Đổi",
"desc": "Biến đổi layer được chọn."
@@ -790,7 +767,6 @@
"hfTokenUnableToVerifyErrorMessage": "Không thể xác minh HuggingFace token. Khả năng cao lỗi mạng. Vui lòng thử lại sau.",
"inplaceInstall": "Tải Xuống Tại Chỗ",
"installRepo": "Tải Xuống Kho Lưu Trữ (Repository)",
"ipAdapters": "IP Adapters",
"loraModels": "LoRA",
"main": "Chính",
"modelConversionFailed": "Chuyển Đổi Model Thất Bại",
@@ -836,7 +812,6 @@
"textualInversions": "Bộ Đảo Ngược Văn Bản",
"loraTriggerPhrases": "Từ Ngữ Kích Hoạt Cho LoRA",
"width": "Chiều Rộng",
"starterModelsInModelManager": "Model khởi đầu có thể tìm thấy ở Trình Quản Lý Model",
"clipLEmbed": "CLIP-L Embed",
"clipGEmbed": "CLIP-G Embed",
"controlLora": "LoRA Điều Khiển Được",
@@ -848,13 +823,11 @@
"sigLip": "SigLIP",
"llavaOnevision": "LLaVA OneVision",
"fileSize": "Kích Thước Tệp",
"filterModels": "Lọc Model",
"modelPickerFallbackNoModelsInstalled2": "Nhấp vào <LinkComponent>Trình Quản Lý Model</LinkComponent> để tải.",
"modelPickerFallbackNoModelsInstalled": "Không Có Sẵn Model.",
"manageModels": "Quản Lý Model",
"hfTokenReset": "Làm Mới HF Token",
"relatedModels": "Model Liên Quan",
"showOnlyRelatedModels": "Liên Quan",
"installedModelsCount": "Đã tải {{installed}} trên {{total}} model.",
"allNModelsInstalled": "Đã tải tất cả {{count}} model",
"nToInstall": "Còn {{count}} để tải",
@@ -871,12 +844,8 @@
"scanFolderDescription": "Quét một thư mục trên máy để tự động tra và tải model.",
"recommendedModels": "Model Khuyến Nghị",
"exploreStarter": "Hoặc duyệt tất cả model khởi đầu có sẵn",
"quickStart": "Gói Khởi Đầu Nhanh",
"bundleDescription": "Các gói đều bao gồm những model cần thiết cho từng nhánh model và những model cơ sở đã chọn lọc để bắt đầu.",
"browseAll": "Hoặc duyệt tất cả model có sẵn:",
"stableDiffusion15": "Stable Diffusion 1.5",
"sdxl": "SDXL",
"fluxDev": "FLUX.1 dev"
"sdxl": "SDXL"
},
"installBundle": "Tải Xuống Gói",
"installBundleMsg1": "Bạn có chắc chắn muốn tải xuống gói {{bundleName}}?",
@@ -887,11 +856,9 @@
"noRecallParameters": "Không tìm thấy tham số",
"imageDetails": "Chi Tiết Ảnh",
"createdBy": "Được Tạo Bởi",
"parsingFailed": "Lỗi Cú Pháp",
"canvasV2Metadata": "Layer Canvas",
"parameterSet": "Dữ liệu tham số {{parameter}}",
"positivePrompt": "Lệnh Tích Cực",
"recallParameter": "Gợi Nhớ {{label}}",
"seed": "Hạt Giống",
"negativePrompt": "Lệnh Tiêu Cực",
"noImageDetails": "Không tìm thấy chí tiết ảnh",
@@ -959,9 +926,7 @@
"strength": "Mức Độ Mạnh Của Sửa Độ Phân Giải Cao",
"method": "Cách Thức Sửa Độ Phân Giải Cao"
},
"hrf": "Sửa Độ Phân Giải Cao",
"enableHrf": "Cho Phép Sửa Độ Phân Giải Cao",
"upscaleMethod": "Cách Thức Upscale"
"hrf": "Sửa Độ Phân Giải Cao"
},
"nodes": {
"validateConnectionsHelp": "Ngăn chặn những kết nối không hợp lý được tạo ra, và đồ thị không hợp lệ bị kích hoạt",
@@ -987,9 +952,7 @@
"float": "Số Thực",
"missingNode": "Thiếu node kích hoạt",
"currentImage": "Hình Ảnh Hiện Tại",
"removeLinearView": "Xoá Khỏi Chế Độ Xem Tuyến Tính",
"unknownErrorValidatingWorkflow": "Lỗi không rõ khi xác thực workflow",
"unableToLoadWorkflow": "Không Thể Tải Workflow",
"workflowSettings": "Cài Đặt Biên Tập Workflow",
"workflowVersion": "Phiên Bản",
"unableToGetWorkflowVersion": "Không thể tìm phiên bản của lược đồ workflow",
@@ -999,7 +962,6 @@
"ipAdapter": "IP Adapter",
"cannotDuplicateConnection": "Không thể tạo hai kết nối trùng lặp",
"workflowValidation": "Lỗi Xác Thực Workflow",
"mismatchedVersion": "Node không hợp lệ: node {{node}} thuộc loại {{type}} có phiên bản không khớp (thử cập nhật?)",
"sourceNodeFieldDoesNotExist": "Kết nối không phù hợp: nguồn/đầu ra của vùng {{node}}.{{field}} không tồn tại",
"targetNodeFieldDoesNotExist": "Kết nối không phù hợp: đích đến/đầu vào của vùng {{node}}.{{field}} không tồn tại",
"missingTemplate": "Node không hợp lệ: node {{node}} thuộc loại {{type}} bị thiếu mẫu trình bày (chưa tải?)",
@@ -1013,7 +975,6 @@
"edge": "Kết Nối",
"graph": "Đồ Thị",
"workflowAuthor": "Tác Giả",
"addLinearView": "Thêm Vào Chế Độ Xem Tuyến Tính",
"showEdgeLabels": "Hiển Thị Tên Kết Nối",
"unknownField": "Vùng Dữ Liệu Không Rõ",
"executionStateCompleted": "Đã Hoàn Tất",
@@ -1043,7 +1004,6 @@
"node": "Node",
"nodeTemplate": "Mẫu Trình Bày Của Node",
"nodeType": "Loại Node",
"noFieldsLinearview": "Không có vùng được thêm vào Chế Độ Xem Tuyến Tính",
"notes": "Ghi Chú",
"updateApp": "Cập Nhật Ứng Dụng",
"updateAllNodes": "Cập Nhật Các Node",
@@ -1051,7 +1011,6 @@
"imageAccessError": "Không thể tìm thấy ảnh {{image_name}}, chuyển về mặc định",
"unknownNode": "Node Không Rõ",
"unknownNodeType": "Loại Node Không Rõ",
"unknownTemplate": "Mẫu Trình Bày Không Rõ",
"cannotConnectOutputToOutput": "Không thế kết nối đầu ra với đầu ra",
"cannotConnectToSelf": "Không thể kết nối với chính nó",
"workflow": "Workflow",
@@ -1067,7 +1026,6 @@
"fitViewportNodes": "Chế Độ Xem Vừa Khớp",
"fullyContainNodes": "Bao Phủ Node Hoàn Toàn Để Chọn",
"fullyContainNodesHelp": "Node phải được phủ kín hoàn toàn trong hộp lựa chọn để được lựa chọn",
"hideLegendNodes": "Ẩn Vùng Nhập",
"hideMinimapnodes": "Ẩn Bản Đồ Thu Nhỏ",
"inputMayOnlyHaveOneConnection": "Đầu vào chỉ có thể có một kết nối",
"noWorkflows": "Không Có Workflow",
@@ -1078,34 +1036,27 @@
"problemSettingTitle": "Có Vấn Đề Khi Thiết Lập Tiêu Đề",
"resetToDefaultValue": "Đặt lại giá trị mặc định",
"reloadNodeTemplates": "Tải Lại Mẫu Trình Bày Node",
"reorderLinearView": "Sắp Xếp Lại Chế Độ Xem Tuyến Tính",
"viewMode": "Dùng Chế Độ Xem Tuyến Tính",
"newWorkflowDesc": "Tạo workflow mới?",
"string": "Chuỗi Ký Tự",
"version": "Phiên Bản",
"versionUnknown": " Phiên Bản Không Rõ",
"workflowContact": "Thông Tin Liên Lạc",
"workflowName": "Tên",
"saveToGallery": "Lưu Vào Thư Viện Ảnh",
"connectionWouldCreateCycle": "Kết nối này sẽ tạo ra vòng lặp",
"addNode": "Thêm Node",
"unsupportedAnyOfLength": "quá nhiều dữ liệu hợp nhất: {{count}}",
"unknownInput": "Đầu Vào Không Rõ: {{name}}",
"validateConnections": "Xác Thực Kết Nối Và Đồ Thị",
"workflowNotes": "Ghi Chú",
"workflowTags": "Nhãn",
"editMode": "Chỉnh sửa trong Trình Biên Tập Workflow",
"edit": "Chỉnh Sửa",
"executionStateInProgress": "Đang Xử Lý",
"showLegendNodes": "Hiển Thị Vùng Nhập",
"outputFieldTypeParseError": "Không thể phân tích loại dữ liệu đầu ra của {{node}}.{{field}} ({{message}})",
"modelAccessError": "Không thể tìm thấy model {{key}}, chuyển về mặc định",
"internalDesc": "Trình kích hoạt này được dùng bên trong bởi Invoke. Nó có thể phá hỏng thay đổi trong khi cập nhật ứng dụng và có thể bị xoá bất cứ lúc nào.",
"specialDesc": "Trình kích hoạt này có một số xử lý đặc biệt trong ứng dụng. Ví dụ, Node Hàng Loạt được dùng để xếp vào nhiều đồ thị từ một workflow.",
"addItem": "Thêm Mục",
"generateValues": "Cho Ra Giá Trị",
"floatRangeGenerator": "Phạm Vị Tạo Ra Số Thực",
"integerRangeGenerator": "Phạm Vị Tạo Ra Số Nguyên",
"linearDistribution": "Phân Bố Tuyến Tính",
"uniformRandomDistribution": "Phân Bố Ngẫu Nhiên Đồng Nhất",
"parseString": "Phân Tích Chuỗi",
@@ -1114,7 +1065,6 @@
"splitOn": "Tách Ở",
"arithmeticSequence": "Cấp Số Cộng",
"generatorNRandomValues_other": "{{count}} giá trị ngẫu nhiên",
"generatorLoading": "đang tải",
"generatorLoadFromFile": "Tải Từ Tệp",
"dynamicPromptsRandom": "Dynamic Prompts (Ngẫu Nhiên)",
"dynamicPromptsCombinatorial": "Dynamic Prompts (Tổ Hợp)",
@@ -1124,7 +1074,6 @@
"description": "Mô Tả",
"loadWorkflowDesc": "Tải workflow?",
"loadWorkflowDesc2": "Workflow hiện tại của bạn có những điều chỉnh chưa được lưu.",
"loadingTemplates": "Đang Tải {{name}}",
"nodeName": "Tên Node",
"unableToUpdateNode": "Cập nhật node thất bại: node {{node}} thuộc dạng {{type}} (có thể cần xóa và tạo lại)",
"downloadWorkflowError": "Lỗi tải xuống workflow",
@@ -1598,9 +1547,7 @@
"concepts": "LoRA",
"loading": "đang tải",
"lora": "LoRA",
"noMatchingLoRAs": "Không có LoRA phù hợp",
"noRefinerModelsInstalled": "Chưa có model SDXL Refiner được tải xuống",
"noLoRAsInstalled": "Chưa có LoRA được tải xuống",
"defaultVAE": "VAE Mặc Định",
"noMatchingModels": "Không có Model phù hợp",
"noModelsAvailable": "Không có model",
@@ -1615,9 +1562,7 @@
"processImage": "Xử Lý Hình Ảnh",
"useSize": "Dùng Kích Thước",
"invoke": {
"fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), chiều rộng hộp giới hạn là {{width}}",
"noModelSelected": "Không có model được lựa chọn",
"fluxModelIncompatibleScaledBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), tỉ lệ chiều dài hộp giới hạn là {{height}}",
"canvasIsFiltering": "Canvas đang bận (đang lọc)",
"canvasIsRasterizing": "Canvas đang bận (đang raster hoá)",
"canvasIsTransforming": "Canvas đang bận (đang biến đổi)",
@@ -1631,8 +1576,6 @@
"systemDisconnected": "Hệ thống mất kết nối",
"invoke": "Kích Hoạt",
"missingNodeTemplate": "Thiếu mẫu trình bày node",
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), chiều dài hộp giới hạn là {{height}}",
"fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), tỉ lệ chiều rộng hộp giới hạn là {{width}}",
"missingInputForField": "thiếu đầu vào",
"missingFieldTemplate": "Thiếu vùng mẫu trình bày",
"collectionTooFewItems": "quá ít mục, tối thiểu là {{minItems}}",
@@ -1647,7 +1590,6 @@
"collectionNumberLTExclusiveMin": "{{value}} <= {{exclusiveMinimum}} (giá trị chọn lọc tối thiểu)",
"collectionNumberGTExclusiveMax": "{{value}} >= {{exclusiveMaximum}} (giá trị chọn lọc tối đa)",
"batchNodeCollectionSizeMismatch": "Kích cỡ tài nguyên không phù hợp với Lô {{batchGroupId}}",
"emptyBatches": "lô trống",
"batchNodeNotConnected": "Node Hàng Loạt chưa được kết nối: {{label}}",
"batchNodeEmptyCollection": "Một vài node hàng loạt có tài nguyên rỗng",
"collectionEmpty": "tài nguyên trống",
@@ -1657,7 +1599,6 @@
"modelIncompatibleScaledBboxHeight": "Chiều dài hộp giới hạn theo tỉ lệ là {{height}} nhưng {{model}} yêu cầu bội số của {{multiple}}",
"modelIncompatibleScaledBboxWidth": "Chiều rộng hộp giới hạn theo tỉ lệ là {{width}} nhưng {{model}} yêu cầu bội số của {{multiple}}",
"modelDisabledForTrial": "Tạo sinh với {{modelName}} là không thể với tài khoản trial. Vào phần thiết lập tài khoản để nâng cấp.",
"fluxKontextMultipleReferenceImages": "Chỉ có thể dùng 1 Ảnh Mẫu cùng lúc với LUX Kontext thông qua BFL API",
"promptExpansionPending": "Trong quá trình mở rộng lệnh",
"promptExpansionResultPending": "Hãy chấp thuận hoặc huỷ bỏ kết quả mở rộng lệnh của bạn"
},
@@ -1706,7 +1647,6 @@
"useAll": "Dùng Tất Cả",
"useCpuNoise": "Dùng Độ Nhiễu CPU",
"remixImage": "Phối Lại Hình Ảnh",
"showOptionsPanel": "Hiển Thị Bảng Bên Cạnh (O hoặc T)",
"shuffle": "Xáo Trộn",
"setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (lớn quá)",
"cfgRescaleMultiplier": "Hệ Số Nhân Thang CFG",
@@ -1716,7 +1656,6 @@
"lockAspectRatio": "Khoá Tỉ Lệ",
"swapDimensions": "Hoán Đổi Kích Thước",
"copyImage": "Sao Chép Hình Ảnh",
"downloadImage": "Tải Xuống Hình Ảnh",
"imageFit": "Căn Chỉnh Ảnh Ban Đầu Thành Kích Thước Đầu Ra",
"info": "Thông Tin",
"usePrompt": "Dùng Lệnh",
@@ -1738,8 +1677,7 @@
"showDynamicPrompts": "HIện Dynamic Prompt",
"maxPrompts": "Số Lệnh Tối Đa",
"promptsPreview": "Xem Trước Lệnh",
"dynamicPrompts": "Dynamic Prompt",
"promptsToGenerate": "Lệnh Để Tạo Sinh"
"dynamicPrompts": "Dynamic Prompt"
},
"settings": {
"beta": "Beta",
@@ -1750,9 +1688,7 @@
"antialiasProgressImages": "Xử Lý Khử Răng Cưa Hình Ảnh",
"models": "Models",
"informationalPopoversDisabledDesc": "Hộp thoại hỗ trợ thông tin đã tắt. Bật lại trong Cài đặt.",
"modelDescriptionsDisabled": "Trình Mô Tả Model Bằng Hộp Thả Đã Tắt",
"enableModelDescriptions": "Bật Trình Mô Tả Model Bằng Hộp Thả",
"modelDescriptionsDisabledDesc": "Trình mô tả model bằng hộp thả đã tắt. Bật lại trong Cài đặt.",
"enableNSFWChecker": "Bật Trình Kiểm Tra NSFW",
"clearIntermediatesWithCount_other": "Dọn sạch {{count}} sản phẩm trung gian",
"reloadingIn": "Tải lại trong",
@@ -1785,13 +1721,9 @@
"refinermodel": "Model Refiner",
"refinerStart": "Bắt Đầu Refiner",
"denoisingStrength": "Sức Mạnh Khử Nhiễu",
"posStylePrompt": "Điểm Tích Cực Cho Lệnh Phong Cách",
"scheduler": "Scheduler",
"refiner": "Refiner",
"cfgScale": "Thang CFG",
"concatPromptStyle": "Liên Kết Lệnh & Phong Cách",
"freePromptStyle": "Viết Lệnh Thủ Công Cho Phong Cách",
"negStylePrompt": "Điểm Tiêu Cực Cho Lệnh Phong Cách",
"negAestheticScore": "Điểm Khác Tiêu Chuẩn",
"noModelsAvailable": "Không có sẵn model"
},
@@ -1808,7 +1740,6 @@
"saveLayerToAssets": "Lưu Layer Vào Khu Tài Nguyên",
"canvas": "Canvas",
"savedToGalleryOk": "Đã Lưu Vào Thư Viện Ảnh",
"addGlobalReferenceImage": "Thêm $t(controlLayers.globalReferenceImage)",
"clipToBbox": "Chuyển Nét Thành Hộp Giới Hạn",
"moveToFront": "Chuyển Lên Trước",
"mergeVisible": "Gộp Layer Đang Hiển Thị",
@@ -1853,7 +1784,6 @@
"stylePrecise": "Phong Cách (Chính Xác)",
"stylePreciseDesc": "Áp dụng cách trình bày chính xác, loại bỏ các chủ thể ảnh hưởng."
},
"deletePrompt": "Xoá Lệnh",
"rasterLayer": "Layer Dạng Raster",
"disableAutoNegative": "Tắt Tự Động Đảo Chiều",
"controlLayer": "Layer Điều Khiển Được",
@@ -1864,8 +1794,6 @@
"replaceLayer": "Thay Đổi Layer",
"regionalGuidance": "Chỉ Dẫn Khu Vực",
"newCanvasFromImage": "Canvas Mới Từ Ảnh",
"rasterLayers_withCount_visible": "Layer Dạng Raster ({{count}})",
"regionalGuidance_withCount_visible": "Chỉ Dẫn Khu Vực ({{count}})",
"convertRasterLayerTo": "Chuyển Đổi $t(controlLayers.rasterLayer) Thành",
"convertControlLayerTo": "Chuyển Đổi $t(controlLayers.controlLayer) Thành",
"convertInpaintMaskTo": "Chuyển Đổi $t(controlLayers.inpaintMask) Thành",
@@ -1876,12 +1804,7 @@
"newRasterLayer": "$t(controlLayers.rasterLayer) Mới",
"enableAutoNegative": "Bật Tự Động Đảo Chiều",
"sendToCanvas": "Chuyển Tới Canvas",
"inpaintMasks_withCount_hidden": "Lớp Phủ Inpaint ({{count}} đang ẩn)",
"globalReferenceImages_withCount_visible": "Ảnh Mẫu Toàn Vùng ({{count}})",
"replaceCurrent": "Thay Đổi Cái Hiện Tại",
"controlLayers_withCount_visible": "Layer Điều Khiển Được ({{count}})",
"hidingType": "Ẩn {{type}}",
"newImg2ImgCanvasFromImage": "Chuyển Đổi Ảnh Sang Ảnh Mới Từ Ảnh",
"copyToClipboard": "Sao Chép Vào Clipboard",
"logDebugInfo": "Thông Tin Log Gỡ Lỗi",
"regionalReferenceImage": "Ảnh Mẫu Khu Vực",
@@ -1900,31 +1823,20 @@
"inpaintMask": "Lớp Phủ Inpaint",
"dynamicGrid": "Lưới Dynamic",
"layer_other": "Layer",
"layer_withCount_other": "Layer ({{count}})",
"pullBboxIntoLayer": "Chuyển Hộp Giới Hạn Vào Layer",
"addInpaintMask": "Thêm $t(controlLayers.inpaintMask)",
"addRegionalGuidance": "Thêm $t(controlLayers.regionalGuidance)",
"sendToGallery": "Đã Chuyển Tới Thư Viện Ảnh",
"unlocked": "Mở Khoá",
"addReferenceImage": "Thêm $t(controlLayers.referenceImage)",
"sendingToCanvas": "Chuyển Ảnh Tạo Sinh Vào Canvas",
"sendingToGallery": "Chuyển Ảnh Tạo Sinh Vào Thư Viện Ảnh",
"viewProgressOnCanvas": "Xem quá trình xử lý và ảnh đầu ra trong <Btn>Canvas</Btn>.",
"inpaintMask_withCount_other": "Lớp Phủ Inpaint",
"regionalGuidance_withCount_other": "Chỉ Dẫn Khu Vực",
"controlLayers_withCount_hidden": "Layer Điều Khiển Được ({{count}} đang ẩn)",
"globalReferenceImages_withCount_hidden": "Ảnh Mẫu Toàn Vùng ({{count}} đang ẩn)",
"rasterLayer_withCount_other": "Layer Dạng Raster",
"globalReferenceImage_withCount_other": "Ảnh Mẫu Toàn Vùng",
"copyRasterLayerTo": "Sao Chép $t(controlLayers.rasterLayer) Tới",
"copyControlLayerTo": "Sao Chép $t(controlLayers.controlLayer) Tới",
"newRegionalGuidance": "$t(controlLayers.regionalGuidance) Mới",
"newGallerySessionDesc": "Nó sẽ dọn sạch canvas và các thiết lập trừ model được chọn. Các ảnh được tạo sinh sẽ được chuyển đến thư viện ảnh.",
"stagingOnCanvas": "Hiển thị hình ảnh lên",
"pullBboxIntoReferenceImage": "Chuyển Hộp Giới Hạn Vào Ảnh Mẫu",
"maskFill": "Lấp Đầy Lớp Phủ",
"addRasterLayer": "Thêm $t(controlLayers.rasterLayer)",
"rasterLayers_withCount_hidden": "Layer Dạng Raster ({{count}} đang ẩn)",
"referenceImage": "Ảnh Mẫu",
"showProgressOnCanvas": "Hiện Quá Trình Xử Lý Lên Canvas",
"prompt": "Lệnh",
@@ -1939,24 +1851,16 @@
},
"addPositivePrompt": "Thêm $t(controlLayers.prompt)",
"deleteReferenceImage": "Xoá Ảnh Mẫu",
"inpaintMasks_withCount_visible": "Lớp Phủ Inpaint ({{count}})",
"disableTransparencyEffect": "Tắt Hiệu Ứng Trong Suốt",
"newGallerySession": "Phiên Thư Viện Ảnh Mới",
"sendToGalleryDesc": "Bấm 'Kích Hoạt' sẽ tiến hành tạo sinh và lưu ảnh vào thư viện ảnh.",
"opacity": "Độ Mờ Đục",
"rectangle": "Hình Chữ Nhật",
"addNegativePrompt": "Thêm $t(controlLayers.negativePrompt)",
"globalReferenceImage": "Ảnh Mẫu Toàn Vùng",
"sendToCanvasDesc": "Bấm 'Kích Hoạt' sẽ hiển thị công việc đang xử lý của bạn lên canvas.",
"viewProgressInViewer": "Xem quá trình xử lý và ảnh đầu ra trong <Btn>Trình Xem Ảnh</Btn>.",
"regionalGuidance_withCount_hidden": "Chỉ Dẫn Khu Vực ({{count}} đang ẩn)",
"controlLayer_withCount_other": "Layer Điều Khiển Được",
"newInpaintMask": "$t(controlLayers.inpaintMask) Mới",
"locked": "Khoá",
"newCanvasSession": "Phiên Canvas Mới",
"transparency": "Độ Trong Suốt",
"showingType": "Hiển Thị {{type}}",
"newCanvasSessionDesc": "Nó sẽ dọn sạch canvas và các thiết lập trừ model được chọn. Các ảnh được tạo sinh sẽ được chuyển đến canvas.",
"selectObject": {
"help2": "Bắt đầu mới một điểm <Bold>Bao Gồm</Bold> trong đối tượng được chọn. Cho thêm điểm để tinh chế phần chọn. Ít điểm hơn thường mang lại kết quả tốt hơn.",
"invertSelection": "Đảo Ngược Phần Chọn",
@@ -2200,7 +2104,6 @@
"newSession": "Phiên Làm Việc Mới",
"resetGenerationSettings": "Khởi Động Lại Cài Đặt Tạo Sinh",
"referenceImageRegional": "Ảnh Mẫu (Khu Vực)",
"referenceImageGlobal": "Ảnh Mẫu (Toàn Vùng)",
"warnings": {
"problemsFound": "Phát hiện vấn đề",
"unsupportedModel": "layer không được hỗ trợ cho model cơ sở này",
@@ -2225,7 +2128,6 @@
"pasteToBboxDesc": "Layer Mới (Trong Hộp Giới Hạn)",
"pasteToCanvas": "Canvas",
"pasteToCanvasDesc": "Layer Mới (Trong Canvas)",
"pastedTo": "Dán Vào {{destination}}",
"regionCopiedToClipboard": "Sao Chép {{region}} Vào Clipboard",
"copyRegionError": "Lỗi khi sao chép {{region}}",
"errors": {
@@ -2245,15 +2147,12 @@
"denoiseLimit": "Giới Hạn Khử Nhiễu",
"addImageNoise": "Thêm $t(controlLayers.imageNoise)",
"referenceImageEmptyStateWithCanvasOptions": "<UploadButton>Tải lên hình ảnh</UploadButton>, kéo ảnh từ thư viện ảnh vào Ảnh Mẫu này, hoặc <PullBboxButton>kéo hộp giới hạn vào Ảnh Mẫu này</PullBboxButton> để bắt đầu.",
"uploadOrDragAnImage": "Kéo ảnh từ thư viện ảnh hoặc <UploadButton>tải lên ảnh</UploadButton>.",
"exportCanvasToPSD": "Xuất Canvas Thành File PSD",
"ruleOfThirds": "Hiển Thị Quy Tắc Một Phần Ba",
"showNonRasterLayers": "Hiển Thị Layer Không Thuộc Dạng Raster (Shift + H)",
"hideNonRasterLayers": "Ẩn Layer Không Thuộc Dạng Raster (Shift + H)",
"autoSwitch": {
"off": "Tắt",
"switchOnStart": "Khi Bắt Đầu",
"switchOnFinish": "Khi Kết Thúc"
"off": "Tắt"
},
"fitBboxToMasks": "Xếp Vừa Hộp Giới Hạn Vào Lớp Phủ",
"invertMask": "Đảo Ngược Lớp Phủ",
@@ -2336,15 +2235,11 @@
"toast": {
"imageUploadFailed": "Tải Lên Ảnh Thất Bại",
"layerCopiedToClipboard": "Sao Chép Layer Vào Clipboard",
"uploadFailedInvalidUploadDesc_withCount_other": "Tối đa là {{count}} ảnh PNG, JPEG hoặc WEBP.",
"imageCopied": "Ảnh Đã Được Sao Chép",
"sentToUpscale": "Chuyển Vào Upscale",
"unableToLoadImage": "Không Thể Tải Hình Ảnh",
"unableToLoadStylePreset": "Không Thể Tải Phong Cách Được Cài Đặt Trước",
"stylePresetLoaded": "Phong Cách Được Cài Đặt Trước Đã Tải",
"imageNotLoadedDesc": "Không thể tìm thấy ảnh",
"imageSaved": "Ảnh Đã Lưu",
"imageSavingFailed": "Lưu Ảnh Thất Bại",
"unableToLoadImageMetadata": "Không Thể Tải Metadata Của Ảnh",
"workflowLoaded": "Workflow Đã Tải",
"uploadFailed": "Tải Lên Thất Bại",
@@ -2356,17 +2251,14 @@
"importFailed": "Nhập Vào Thất Bại",
"importSuccessful": "Nhập Vào Thành Công",
"workflowDeleted": "Workflow Đã Xoá",
"setControlImage": "Đặt làm ảnh điều khiển được",
"connected": "Kết Nối Đến Server",
"imageUploaded": "Ảnh Đã Được Tải Lên",
"invalidUpload": "Dữ Liệu Tải Lên Không Hợp Lệ",
"modelImportCanceled": "Nhập Vào Model Thất Bại",
"parameters": "Tham Số",
"parameterSet": "Gợi Lại Tham Số",
"parameterSetDesc": "Gợi lại {{parameter}}",
"loadedWithWarnings": "Đã Tải Workflow Với Cảnh Báo",
"outOfMemoryErrorDesc": "Thiết lập tạo sinh hiện tại đã vượt mức cho phép của thiết bị. Hãy điều chỉnh thiết lập và thử lại.",
"setNodeField": "Đặt làm vùng node",
"problemRetrievingWorkflow": "Có Vấn Đề Khi Lấy Lại Workflow",
"somethingWentWrong": "Có Vấn Đề Phát Sinh",
"problemDeletingWorkflow": "Có Vấn Đề Khi Xoá Workflow",
@@ -2382,7 +2274,6 @@
"addedToUncategorized": "Thêm vào tài nguyên của bảng $t(boards.uncategorized)",
"linkCopied": "Đường Liên Kết Đã Được Sao Chép",
"outOfMemoryError": "Lỗi Vượt Quá Bộ Nhớ",
"layerSavedToAssets": "Lưu Layer Vào Khu Tài Nguyên",
"modelAddedSimple": "Đã Thêm Model Vào Hàng Đợi",
"parametersSet": "Tham Số Đã Được Gợi Lại",
"parameterNotSetDesc": "Không thể gợi lại {{parameter}}",
@@ -2403,32 +2294,22 @@
"chatGPT4oIncompatibleGenerationMode": "ChatGPT 4o chỉ hỗ trợ Từ Ngữ Sang Hình Ảnh và Hình Ảnh Sang Hình Ảnh. Hãy dùng model khác cho các tác vụ Inpaint và Outpaint.",
"imagenIncompatibleGenerationMode": "Google {{model}} chỉ hỗ trợ Từ Ngữ Sang Hình Ảnh. Dùng các model khác cho Hình Ảnh Sang Hình Ảnh, Inpaint và Outpaint.",
"fluxKontextIncompatibleGenerationMode": "FLUX Kontext không hỗ trợ tạo sinh từ hình ảnh từ canvas. Thử sử dụng Ảnh Mẫu và tắt các Layer Dạng Raster.",
"noRasterLayers": "Không Tìm Thấy Layer Dạng Raster",
"noRasterLayersDesc": "Tạo ít nhất một layer dạng raster để xuất file PSD",
"noActiveRasterLayers": "Không Có Layer Dạng Raster Hoạt Động",
"noActiveRasterLayersDesc": "Khởi động ít nhất một layer dạng raster để xuất file PSD",
"noVisibleRasterLayers": "Không Có Layer Dạng Raster Hiển Thị",
"noVisibleRasterLayersDesc": "Khởi động ít nhất một layer dạng raster để xuất file PSD",
"invalidCanvasDimensions": "Kích Thước Canvas Không Phù Hợp",
"canvasTooLarge": "Canvas Quá Lớn",
"canvasTooLargeDesc": "Kích thước canvas vượt mức tối đa cho phép để xuất file PSD. Giảm cả chiều dài và chiều rộng chủa canvas và thử lại.",
"failedToProcessLayers": "Thất Bại Khi Xử Lý Layer",
"psdExportSuccess": "Xuất File PSD Hoàn Tất",
"psdExportSuccessDesc": "Thành công xuất {{count}} layer sang file PSD",
"problemExportingPSD": "Có Vấn Đề Khi Xuất File PSD",
"canvasManagerNotAvailable": "Trình Quản Lý Canvas Không Có Sẵn",
"noValidLayerAdapters": "Không có Layer Adaper Phù Hợp",
"promptGenerationStarted": "Trình tạo sinh lệnh khởi động",
"uploadAndPromptGenerationFailed": "Thất bại khi tải lên ảnh để tạo sinh lệnh",
"promptExpansionFailed": "Có vấn đề xảy ra. Hãy thử mở rộng lệnh lại.",
"maskInverted": "Đã Đảo Ngược Lớp Phủ",
"maskInvertFailed": "Thất Bại Khi Đảo Ngược Lớp Phủ",
"noVisibleMasks": "Không Có Lớp Phủ Đang Hiển Thị",
"noVisibleMasksDesc": "Tạo hoặc bật ít nhất một lớp phủ inpaint để đảo ngược",
"noInpaintMaskSelected": "Không Có Lớp Phủ Inpant Được Chọn",
"noInpaintMaskSelectedDesc": "Chọn một lớp phủ inpaint để đảo ngược",
"invalidBbox": "Hộp Giới Hạn Không Hợp Lệ",
"invalidBboxDesc": "Hợp giới hạn có kích thước không hợp lệ"
"noVisibleMasksDesc": "Tạo hoặc bật ít nhất một lớp phủ inpaint để đảo ngược"
},
"ui": {
"tabs": {
@@ -2540,28 +2421,20 @@
"saveWorkflowAs": "Lưu Workflow Như",
"downloadWorkflow": "Lưu Vào Tệp",
"noWorkflows": "Không Có Workflow",
"problemLoading": "Có Vấn Đề Khi Tải Workflow",
"clearWorkflowSearchFilter": "Xoá Workflow Khỏi Bộ Lọc Tìm Kiếm",
"defaultWorkflows": "Workflow Mặc Định",
"userWorkflows": "Workflow Của Người Dùng",
"projectWorkflows": "Dự Án Workflow",
"savingWorkflow": "Đang Lưu Workflow...",
"ascending": "Tăng Dần",
"loading": "Đang Tải Workflow",
"chooseWorkflowFromLibrary": "Chọn Workflow Từ Thư Viện",
"workflows": "Workflow",
"copyShareLinkForWorkflow": "Sao Chép Liên Kết Chia Sẻ Cho Workflow",
"openWorkflow": "Mở Workflow",
"name": "Tên",
"unnamedWorkflow": "Workflow Vô Danh",
"saveWorkflow": "Lưu Workflow",
"problemSavingWorkflow": "Có Vấn Đề Khi Lưu Workflow",
"noDescription": "Không có mô tả",
"updated": "Đã Cập Nhật",
"uploadWorkflow": "Tải Từ Tệp",
"autoLayout": "Bố Trí Tự Động",
"loadWorkflow": "$t(common.load) Workflow",
"searchWorkflows": "Tìm Workflow",
"newWorkflowCreated": "Workflow Mới Được Tạo",
"workflowCleared": "Đã Dọn Dẹp Workflow",
"loadFromGraph": "Tải Workflow Từ Đồ Thị",
@@ -2572,7 +2445,6 @@
"opened": "Đã Mở",
"deleteWorkflow": "Xoá Workflow",
"workflowEditorMenu": "Menu Biên Tập Workflow",
"openLibrary": "Mở Thư Viện",
"builder": {
"resetAllNodeFields": "Tải Lại Các Vùng Node",
"builder": "Trình Tạo Vùng Nhập",
@@ -2588,7 +2460,6 @@
"multiLine": "Nhiều Dòng",
"slider": "Thanh Trượt",
"both": "Cả Hai",
"emptyRootPlaceholderViewMode": "Chọn Chỉnh Sửa để bắt đầu tạo nên một vùng nhập cho workflow này.",
"emptyRootPlaceholderEditMode": "Kéo thành phần vùng nhập hoặc vùng node vào đây để bắt đầu.",
"containerPlaceholder": "Hộp Chứa Trống",
"headingPlaceholder": "Đầu Dòng Trống",
@@ -2597,7 +2468,6 @@
"deleteAllElements": "Xóa Tất Cả Thành Phần",
"nodeField": "Vùng Node",
"nodeFieldTooltip": "Để thêm vùng node, bấm vào dấu cộng nhỏ trên vùng trong Trình Biên Tập Workflow, hoặc kéo vùng theo tên của nó vào vùng nhập.",
"workflowBuilderAlphaWarning": "Trình tạo vùng nhập đang trong giai đoạn alpha. Nó có thể xuất hiện những thay đổi đột ngột trước khi chính thức được phát hành.",
"container": "Hộp Chứa",
"heading": "Đầu Dòng",
"text": "Văn Bản",
@@ -2649,16 +2519,13 @@
"browseWorkflows": "Khám Phá Workflow",
"workflowThumbnail": "Ảnh Minh Họa Workflow",
"saveChanges": "Lưu Thay Đổi",
"allLoaded": "Đã Tải Tất Cả Workflow",
"shared": "Nhóm",
"searchPlaceholder": "Tìm theo tên, mô tả, hoặc nhãn",
"filterByTags": "Lọc Theo Nhãn",
"recentlyOpened": "Mở Gần Đây",
"private": "Cá Nhân",
"loadMore": "Tải Thêm",
"view": "Xem",
"deselectAll": "Huỷ Chọn Tất Cả",
"noRecentWorkflows": "Không Có Workflows Gần Đây",
"recommended": "Có Thể Bạn Sẽ Cần",
"emptyStringPlaceholder": "<xâu ký tự trống>",
"published": "Đã Đăng"
@@ -2698,7 +2565,6 @@
"whatsNewInInvoke": "Có Gì Mới Ở Invoke",
"readReleaseNotes": "Đọc Ghi Chú Phát Hành",
"watchRecentReleaseVideos": "Xem Video Phát Hành Mới Nhất",
"watchUiUpdatesOverview": "Xem Tổng Quan Về Những Cập Nhật Cho Giao Diện Người Dùng",
"items": [
"Misc QoL: Bật/Tắt hiển thị hộp giới hạn, đánh dấu node bị lỗi, chặn lỗi thêm node vào vùng nhập nhiều lần, khả năng đọc lại metadata của CLIP Skip",
"Giảm lượng tiêu thụ VRAM cho các ảnh mẫu Kontext và mã hóa VAE"

View File

@@ -25,7 +25,6 @@
"batch": "批次管理器",
"communityLabel": "社区",
"modelManager": "模型管理器",
"imageFailedToLoad": "无法加载图像",
"learnMore": "了解更多",
"advanced": "高级",
"t2iAdapter": "T2I Adapter",
@@ -51,23 +50,19 @@
"somethingWentWrong": "出了点问题",
"copyError": "$t(gallery.copy) 错误",
"input": "输入",
"notInstalled": "非 $t(common.installed)",
"delete": "删除",
"updated": "已上传",
"save": "保存",
"created": "已创建",
"prevPage": "上一页",
"unknownError": "未知错误",
"direction": "指向",
"orderBy": "排序方式:",
"nextPage": "下一页",
"saveAs": "保存为",
"ai": "ai",
"or": "或",
"aboutDesc": "使用 Invoke 工作?来看看:",
"add": "添加",
"copy": "复制",
"localSystem": "本地系统",
"aboutHeading": "掌握你的创造力",
"enabled": "已启用",
"disabled": "已禁用",
@@ -78,7 +73,6 @@
"selected": "选中的",
"green": "绿",
"blue": "蓝",
"goTo": "前往",
"dontShowMeThese": "请勿显示这些内容",
"beta": "测试版",
"toResolve": "解决",
@@ -104,13 +98,11 @@
"galleryImageSize": "预览大小",
"gallerySettings": "预览设置",
"autoSwitchNewImages": "自动切换到新图像",
"noImagesInGallery": "无图像可用于显示",
"deleteImage_other": "删除{{count}}张图片",
"deleteImagePermanent": "删除的图片无法被恢复。",
"autoAssignBoardOnClick": "点击后自动分配面板",
"featuresWillReset": "如果您删除该图像,这些功能会立即被重置。",
"loading": "加载中",
"unableToLoad": "无法加载图库",
"currentlyInUse": "该图像目前在以下功能中使用:",
"copy": "复制",
"download": "下载",
@@ -125,7 +117,6 @@
"starImage": "收藏图像",
"alwaysShowImageSizeBadge": "始终显示图像尺寸",
"selectForCompare": "选择以比较",
"selectAnImageToCompare": "选择一个图像进行比较",
"slider": "滑块",
"sideBySide": "并排",
"bulkDownloadFailed": "下载失败",
@@ -148,7 +139,6 @@
"newestFirst": "最新在前",
"compareHelp4": "按 <Kbd>Z</Kbd>或 <Kbd>Esc</Kbd> 键退出。",
"searchImages": "按元数据搜索",
"jump": "跳过",
"compareHelp2": "按 <Kbd>M</Kbd> 键切换不同的比较模式。",
"displayBoardSearch": "板块搜索",
"displaySearch": "图像搜索",
@@ -161,8 +151,6 @@
"gallery": "画廊",
"move": "移动",
"imagesTab": "您在Invoke中创建和保存的图片。",
"openViewer": "打开查看器",
"closeViewer": "关闭查看器",
"assetsTab": "您已上传用于项目的文件。"
},
"hotkeys": {
@@ -310,10 +298,6 @@
"title": "移动工具",
"desc": "选择移动工具。"
},
"setFillToWhite": {
"title": "将颜色设置为白色",
"desc": "将当前工具的颜色设置为白色。"
},
"cancelTransform": {
"desc": "取消待处理的变换。",
"title": "取消变换"
@@ -577,9 +561,7 @@
"huggingFacePlaceholder": "所有者或模型名称",
"huggingFaceRepoID": "HuggingFace仓库ID",
"loraTriggerPhrases": "LoRA 触发词",
"ipAdapters": "IP适配器",
"spandrelImageToImage": "图生图(Spandrel)",
"starterModelsInModelManager": "您可以在模型管理器中找到初始模型",
"noDefaultSettings": "此模型没有配置默认设置。请访问模型管理器添加默认设置。",
"clipEmbed": "CLIP 嵌入",
"defaultSettingsOutOfSync": "某些设置与模型的默认值不匹配:",
@@ -630,12 +612,10 @@
"scaledHeight": "缩放长度",
"infillMethod": "填充方法",
"tileSize": "方格尺寸",
"downloadImage": "下载图像",
"usePrompt": "使用提示",
"useSeed": "使用种子",
"useAll": "使用所有参数",
"info": "信息",
"showOptionsPanel": "显示侧栏浮窗 (O 或 T)",
"seamlessYAxis": "无缝平铺 Y 轴",
"seamlessXAxis": "无缝平铺 X 轴",
"denoisingStrength": "去噪强度",
@@ -661,15 +641,11 @@
"addingImagesTo": "添加图像到",
"noPrompts": "没有已生成的提示词",
"canvasIsFiltering": "画布正在过滤",
"fluxModelIncompatibleScaledBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16),缩放后的边界框高度为 {{height}}",
"noCLIPEmbedModelSelected": "未为FLUX生成选择CLIP嵌入模型",
"noFLUXVAEModelSelected": "未为FLUX生成选择VAE模型",
"canvasIsRasterizing": "画布正在栅格化",
"canvasIsCompositing": "画布正在合成",
"fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16),边界框宽度为 {{width}}",
"fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16),缩放后的边界框宽度为 {{width}}",
"noT5EncoderModelSelected": "未为FLUX生成选择T5编码器模型",
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16),边界框高度为 {{height}}",
"canvasIsTransforming": "画布正在变换"
},
"patchmatchDownScaleSize": "缩小",
@@ -733,8 +709,6 @@
"informationalPopoversDisabledDesc": "信息提示框已被禁用.请在设置中重新启用.",
"enableModelDescriptions": "在下拉菜单中启用模型描述",
"confirmOnNewSession": "新会话时确认",
"modelDescriptionsDisabledDesc": "下拉菜单中的模型描述已被禁用。可在设置中启用。",
"modelDescriptionsDisabled": "下拉菜单中的模型描述已禁用",
"showDetailedInvocationProgress": "显示进度详情"
},
"toast": {
@@ -750,14 +724,11 @@
"problemCopyingImage": "无法复制图像",
"modelAddedSimple": "模型已加入队列",
"loadedWithWarnings": "已加载带有警告的工作流",
"setControlImage": "设为控制图像",
"setNodeField": "设为节点字段",
"imageUploaded": "图像已上传",
"addedToBoard": "添加到{{name}}的资产中",
"workflowLoaded": "工作流已加载",
"imageUploadFailed": "图像上传失败",
"baseModelChangedCleared_other": "已清除或禁用{{count}}个不兼容的子模型",
"invalidUpload": "无效的上传",
"problemDeletingWorkflow": "删除工作流时出现问题",
"workflowDeleted": "已删除工作流",
"problemRetrievingWorkflow": "检索工作流时发生问题",
@@ -777,21 +748,16 @@
"modelImportCanceled": "模型导入已取消",
"importFailed": "导入失败",
"importSuccessful": "导入成功",
"layerSavedToAssets": "图层已保存到资产",
"sentToUpscale": "已发送到放大处理",
"addedToUncategorized": "已添加到看板 $t(boards.uncategorized) 的资产中",
"linkCopied": "链接已复制",
"uploadFailedInvalidUploadDesc_withCount_other": "最多只能上传 {{count}} 张 PNG 或 JPEG 图像。",
"problemSavingLayer": "无法保存图层",
"unableToLoadImage": "无法加载图像",
"imageNotLoadedDesc": "无法找到图像",
"unableToLoadStylePreset": "无法加载样式预设",
"stylePresetLoaded": "样式预设已加载",
"problemCopyingLayer": "无法复制图层",
"sentToCanvas": "已发送到画布",
"unableToLoadImageMetadata": "无法加载图像元数据",
"imageSaved": "图像已保存",
"imageSavingFailed": "图像保存失败",
"layerCopiedToClipboard": "图层已复制到剪贴板",
"imagesWillBeAddedTo": "上传的图像将添加到看板 {{boardName}} 的资产中。"
},
@@ -819,11 +785,8 @@
"fitViewportNodes": "自适应视图",
"showMinimapnodes": "显示缩略图",
"hideMinimapnodes": "隐藏缩略图",
"showLegendNodes": "显示字段类型图例",
"hideLegendNodes": "隐藏字段类型图例",
"downloadWorkflow": "下载工作流 JSON",
"workflowDescription": "简述",
"versionUnknown": " 未知版本",
"noNodeSelected": "无选中的节点",
"addNode": "添加节点",
"unableToValidateWorkflow": "无法验证工作流",
@@ -833,9 +796,7 @@
"workflowContact": "联系",
"animatedEdges": "边缘动效",
"nodeTemplate": "节点模板",
"unableToLoadWorkflow": "无法加载工作流",
"snapToGrid": "对齐网格",
"noFieldsLinearview": "线性视图中未添加任何字段",
"nodeSearch": "检索节点",
"version": "版本",
"validateConnections": "验证连接和节点图",
@@ -850,8 +811,6 @@
"fieldTypesMustMatch": "类型必须匹配",
"workflow": "工作流",
"animatedEdgesHelp": "为选中边缘和其连接的选中节点的边缘添加动画",
"unknownTemplate": "未知模板",
"removeLinearView": "从线性视图中移除",
"workflowTags": "标签",
"fullyContainNodesHelp": "节点必须完全位于选择框中才能被选中",
"workflowValidation": "工作流验证错误",
@@ -885,7 +844,6 @@
"node": "节点",
"collection": "合集",
"string": "字符串",
"mismatchedVersion": "无效的节点:类型为 {{type}} 的节点 {{node}} 版本不匹配(是否尝试更新?)",
"cannotDuplicateConnection": "无法创建重复的连接",
"enum": "Enum (枚举)",
"float": "浮点",
@@ -896,7 +854,6 @@
"unableToUpdateNodes_other": "{{count}} 个节点无法完成更新",
"inputFieldTypeParseError": "无法解析 {{node}} 的输入类型 {{field}}。({{message}})",
"unsupportedArrayItemType": "不支持的数组类型 \"{{type}}\"",
"addLinearView": "添加到线性视图",
"targetNodeFieldDoesNotExist": "无效的边缘:{{node}} 的目标/输入区域 {{field}} 不存在",
"unsupportedMismatchedUnion": "合集或标量类型与基类 {{firstType}} 和 {{secondType}} 不匹配",
"allNodesUpdated": "已更新所有节点",
@@ -916,7 +873,6 @@
"collectionOrScalarFieldType": "{{name}} (单一项目或项目集合)",
"nodeVersion": "节点版本",
"deletedInvalidEdge": "已删除无效的边缘 {{source}} -> {{target}}",
"unknownInput": "未知输入:{{name}}",
"prototypeDesc": "此调用是一个原型 (prototype)。它可能会在本项目更新期间发生破坏性更改,并且随时可能被删除。",
"betaDesc": "此调用尚处于测试阶段。在稳定之前,它可能会在项目更新期间发生破坏性更改。本项目计划长期支持这种调用。",
"newWorkflow": "新建工作流",
@@ -928,7 +884,6 @@
"missingNode": "缺少调用节点",
"missingInvocationTemplate": "缺少调用模版",
"noFieldsViewMode": "此工作流程未选择任何要显示的字段.请查看完整工作流程以进行配置.",
"reorderLinearView": "调整线性视图顺序",
"viewMode": "在线性视图中使用",
"showEdgeLabelsHelp": "在边缘上显示标签,指示连接的节点",
"cannotMixAndMatchCollectionItemTypes": "集合项目类型不能混用",
@@ -1021,18 +976,14 @@
"refinerStart": "Refiner 开始作用时机",
"scheduler": "调度器",
"cfgScale": "CFG 等级",
"negStylePrompt": "负向样式提示词",
"noModelsAvailable": "无可用模型",
"negAestheticScore": "负向美学评分",
"denoisingStrength": "去噪强度",
"refinermodel": "Refiner 模型",
"posAestheticScore": "正向美学评分",
"concatPromptStyle": "链接提示词 & 样式",
"loading": "加载中...",
"steps": "步数",
"posStylePrompt": "正向样式提示词",
"refiner": "Refiner",
"freePromptStyle": "手动输入样式提示词",
"refinerSteps": "精炼步数"
},
"metadata": {
@@ -1059,8 +1010,6 @@
"vae": "VAE",
"cfgRescaleMultiplier": "$t(parameters.cfgRescaleMultiplier)",
"allPrompts": "所有提示",
"parsingFailed": "解析失败",
"recallParameter": "调用{{label}}",
"imageDimensions": "图像尺寸",
"parameterSet": "已设置参数{{parameter}}",
"guidance": "指导",
@@ -1071,11 +1020,9 @@
"models": {
"noMatchingModels": "无相匹配的模型",
"loading": "加载中",
"noMatchingLoRAs": "无相匹配的 LoRA",
"noModelsAvailable": "无可用模型",
"selectModel": "选择一个模型",
"noRefinerModelsInstalled": "无已安装的 SDXL Refiner 模型",
"noLoRAsInstalled": "无已安装的 LoRA",
"addLora": "添加 LoRA",
"lora": "LoRA",
"defaultVAE": "默认 VAE",
@@ -1104,10 +1051,8 @@
"deletedBoardsCannotbeRestored": "删除的面板无法恢复。选择“仅删除面板”选项后,相关图片将会被移至未分类区域。",
"movingImagesToBoard_other": "移动 {{count}} 张图像到面板:",
"selectedForAutoAdd": "已选中自动添加",
"hideBoards": "隐藏面板",
"noBoards": "没有{{boardType}}类型的面板",
"unarchiveBoard": "恢复面板",
"viewBoards": "查看面板",
"addPrivateBoard": "创建私密面板",
"addSharedBoard": "创建共享面板",
"boards": "面板",
@@ -1576,8 +1521,6 @@
"useCache": "使用缓存"
},
"hrf": {
"enableHrf": "启用高分辨率修复",
"upscaleMethod": "放大方法",
"metadata": {
"strength": "高分辨率修复强度",
"enabled": "高分辨率修复已启用",
@@ -1590,20 +1533,15 @@
"workflowEditorMenu": "工作流编辑器菜单",
"workflowName": "工作流名称",
"saveWorkflow": "保存工作流",
"openWorkflow": "打开工作流",
"clearWorkflowSearchFilter": "清除工作流检索过滤器",
"workflowLibrary": "工作流库",
"downloadWorkflow": "保存到文件",
"workflowSaved": "已保存工作流",
"unnamedWorkflow": "未命名的工作流",
"savingWorkflow": "保存工作流中...",
"problemLoading": "加载工作流时出现问题",
"loading": "加载工作流中",
"searchWorkflows": "检索工作流",
"problemSavingWorkflow": "保存工作流时出现问题",
"deleteWorkflow": "删除工作流",
"workflows": "工作流",
"noDescription": "无描述",
"uploadWorkflow": "从文件中加载",
"newWorkflowCreated": "已创建新的工作流",
"name": "名称",
@@ -1623,9 +1561,6 @@
"copyShareLinkForWorkflow": "复制工作流程的分享链接",
"delete": "删除",
"download": "下载",
"defaultWorkflows": "默认工作流程",
"userWorkflows": "用户工作流程",
"projectWorkflows": "项目工作流程",
"copyShareLink": "复制分享链接",
"chooseWorkflowFromLibrary": "从库中选择工作流程",
"deleteWorkflow2": "您确定要删除此工作流程吗?此操作无法撤销。"
@@ -1663,7 +1598,6 @@
"moveToBack": "移动到后面",
"moveToFront": "移动到前面",
"addLayer": "添加层",
"deletePrompt": "删除提示词",
"addPositivePrompt": "添加 $t(controlLayers.prompt)",
"addNegativePrompt": "添加 $t(controlLayers.negativePrompt)",
"rectangle": "矩形",
@@ -1687,7 +1621,6 @@
"maskFill": "遮罩填充",
"newCanvasFromImage": "从图像创建新画布",
"pullBboxIntoReferenceImageOk": "边界框已导入到参考图像",
"globalReferenceImage_withCount_other": "全局参考图像",
"addInpaintMask": "添加 $t(controlLayers.inpaintMask)",
"referenceImage": "参考图像",
"globalReferenceImage": "全局参考图像",
@@ -1696,14 +1629,10 @@
"copyRasterLayerTo": "复制 $t(controlLayers.rasterLayer) 到",
"clearHistory": "清除历史记录",
"inpaintMask": "修复遮罩",
"regionalGuidance_withCount_visible": "区域引导({{count}} 个)",
"inpaintMasks_withCount_hidden": "修复遮罩({{count}} 个已隐藏)",
"enableAutoNegative": "启用自动负面提示",
"disableAutoNegative": "禁用自动负面提示",
"deleteReferenceImage": "删除参考图像",
"sendToCanvas": "发送到画布",
"controlLayers_withCount_visible": "控制图层({{count}} 个)",
"rasterLayers_withCount_visible": "栅格图层({{count}} 个)",
"convertRegionalGuidanceTo": "将 $t(controlLayers.regionalGuidance) 转换为",
"newInpaintMask": "新建 $t(controlLayers.inpaintMask)",
"regionIsEmpty": "选定区域为空",
@@ -1715,14 +1644,12 @@
"addRasterLayer": "添加 $t(controlLayers.rasterLayer)",
"newRasterLayerOk": "已创建栅格层",
"newRasterLayerError": "创建栅格层时出现问题",
"inpaintMasks_withCount_visible": "修复遮罩({{count}} 个)",
"convertRasterLayerTo": "将 $t(controlLayers.rasterLayer) 转换为",
"copyControlLayerTo": "复制 $t(controlLayers.controlLayer) 到",
"copyInpaintMaskTo": "复制 $t(controlLayers.inpaintMask) 到",
"copyRegionalGuidanceTo": "复制 $t(controlLayers.regionalGuidance) 到",
"newRasterLayer": "新建 $t(controlLayers.rasterLayer)",
"newControlLayer": "新建 $t(controlLayers.controlLayer)",
"newImg2ImgCanvasFromImage": "从图像创建新的图生图",
"rasterLayer": "栅格层",
"controlLayer": "控制层",
"outputOnlyMaskedRegions": "仅输出生成的区域",
@@ -1735,36 +1662,22 @@
"bboxOverlay": "显示边界框覆盖层",
"clipToBbox": "将Clip限制到边界框",
"width": "宽度",
"addGlobalReferenceImage": "添加 $t(controlLayers.globalReferenceImage)",
"inpaintMask_withCount_other": "修复遮罩",
"regionalGuidance_withCount_other": "区域引导",
"newRegionalReferenceImageError": "创建局部参考图像时出现问题",
"pullBboxIntoLayerError": "将边界框导入图层时出现问题",
"pullBboxIntoLayerOk": "边界框已导入到图层",
"sendToCanvasDesc": "按下“Invoke”按钮会将您的工作进度暂存到画布上。",
"sendToGallery": "发送到图库",
"sendToGalleryDesc": "按下“Invoke”键会生成并保存一张唯一的图像到您的图库中。",
"rasterLayer_withCount_other": "栅格图层",
"mergeDown": "向下合并",
"clearCaches": "清除缓存",
"recalculateRects": "重新计算矩形",
"duplicate": "复制",
"regionalGuidance_withCount_hidden": "区域引导({{count}} 个已隐藏)",
"convertControlLayerTo": "将 $t(controlLayers.controlLayer) 转换为",
"convertInpaintMaskTo": "将 $t(controlLayers.inpaintMask) 转换为",
"viewProgressInViewer": "在 <Btn>图像查看器</Btn> 中查看进度和输出结果。",
"viewProgressOnCanvas": "在 <Btn>画布</Btn> 上查看进度和暂存的输出内容。",
"sendingToGallery": "将生成内容发送到图库",
"copyToClipboard": "复制到剪贴板",
"controlLayer_withCount_other": "控制图层",
"sendingToCanvas": "在画布上准备生成",
"addReferenceImage": "添加 $t(controlLayers.referenceImage)",
"addRegionalGuidance": "添加 $t(controlLayers.regionalGuidance)",
"controlLayers_withCount_hidden": "控制图层({{count}} 个已隐藏)",
"rasterLayers_withCount_hidden": "栅格图层({{count}} 个已隐藏)",
"globalReferenceImages_withCount_hidden": "全局参考图像({{count}} 个已隐藏)",
"globalReferenceImages_withCount_visible": "全局参考图像({{count}} 个)",
"layer_withCount_other": "图层({{count}} 个)",
"enableTransparencyEffect": "启用透明效果",
"disableTransparencyEffect": "禁用透明效果",
"hidingType": "隐藏 {{type}}",

View File

@@ -19,7 +19,6 @@
"folder": "資料夾",
"installed": "已安裝",
"accept": "接受",
"goTo": "前往",
"input": "輸入",
"random": "隨機",
"selected": "已選擇",
@@ -29,8 +28,7 @@
"copy": "複製",
"error": "錯誤",
"file": "檔案",
"format": "格式",
"imageFailedToLoad": "無法載入圖片"
"format": "格式"
},
"accessibility": {
"invokeProgressBar": "Invoke 進度條",
@@ -179,8 +177,7 @@
"workflowAuthor": "作者",
"version": "版本",
"executionStateCompleted": "已完成",
"edge": "邊緣",
"versionUnknown": " 版本未知"
"edge": "邊緣"
},
"sdxl": {
"steps": "步數",

View File

@@ -2,12 +2,12 @@ import { useAppSelector } from 'app/store/storeHooks';
import { useIsRegionFocused } from 'common/hooks/focus';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { useLoadWorkflow } from 'features/gallery/hooks/useLoadWorkflow';
import { useRecallAll } from 'features/gallery/hooks/useRecallAll';
import { useRecallAll } from 'features/gallery/hooks/useRecallAllImageMetadata';
import { useRecallDimensions } from 'features/gallery/hooks/useRecallDimensions';
import { useRecallPrompts } from 'features/gallery/hooks/useRecallPrompts';
import { useRecallRemix } from 'features/gallery/hooks/useRecallRemix';
import { useRecallSeed } from 'features/gallery/hooks/useRecallSeed';
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
import { selectLastSelectedItem } from 'features/gallery/store/gallerySelectors';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo } from 'react';
import { useImageDTO } from 'services/api/endpoints/images';
@@ -15,8 +15,8 @@ import type { ImageDTO } from 'services/api/types';
export const GlobalImageHotkeys = memo(() => {
useAssertSingleton('GlobalImageHotkeys');
const imageName = useAppSelector(selectLastSelectedImage);
const imageDTO = useImageDTO(imageName);
const lastSelectedItem = useAppSelector(selectLastSelectedItem);
const imageDTO = useImageDTO(lastSelectedItem?.type === 'image' ? lastSelectedItem.id : null);
if (!imageDTO) {
return null;

View File

@@ -3,10 +3,12 @@ import ChangeBoardModal from 'features/changeBoardModal/components/ChangeBoardMo
import { CanvasPasteModal } from 'features/controlLayers/components/CanvasPasteModal';
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { DeleteImageModal } from 'features/deleteImageModal/components/DeleteImageModal';
import { DeleteVideoModal } from 'features/deleteVideoModal/components/DeleteVideoModal';
import { FullscreenDropzone } from 'features/dnd/FullscreenDropzone';
import { DynamicPromptsModal } from 'features/dynamicPrompts/components/DynamicPromptsPreviewModal';
import DeleteBoardModal from 'features/gallery/components/Boards/DeleteBoardModal';
import { ImageContextMenu } from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
import { ImageContextMenu } from 'features/gallery/components/ContextMenu/ImageContextMenu';
import { VideoContextMenu } from 'features/gallery/components/ContextMenu/VideoContextMenu';
import { ShareWorkflowModal } from 'features/nodes/components/sidePanel/workflow/WorkflowLibrary/ShareWorkflowModal';
import { WorkflowLibraryModal } from 'features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal';
import { CancelAllExceptCurrentQueueItemConfirmationAlertDialog } from 'features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog';
@@ -31,6 +33,7 @@ export const GlobalModalIsolator = memo(() => {
return (
<>
<DeleteImageModal />
<DeleteVideoModal />
<ChangeBoardModal />
<DynamicPromptsModal />
<StylePresetModal />
@@ -47,6 +50,7 @@ export const GlobalModalIsolator = memo(() => {
<DeleteBoardModal />
<GlobalImageHotkeys />
<ImageContextMenu />
<VideoContextMenu />
<FullscreenDropzone />
<VideosModal />
<SaveWorkflowAsDialog />

View File

@@ -1,16 +1,14 @@
import 'i18n';
import type { Middleware } from '@reduxjs/toolkit';
import type { StudioInitAction } from 'app/hooks/useStudioInitAction';
import type { InvokeAIUIProps } from 'app/components/types';
import { $didStudioInit } from 'app/hooks/useStudioInitAction';
import type { LoggingOverrides } from 'app/logging/logger';
import { $loggingOverrides, configureLogging } from 'app/logging/logger';
import { addStorageListeners } from 'app/store/enhancers/reduxRemember/driver';
import { $accountSettingsLink } from 'app/store/nanostores/accountSettingsLink';
import { $accountTypeText } from 'app/store/nanostores/accountTypeText';
import { $authToken } from 'app/store/nanostores/authToken';
import { $baseUrl } from 'app/store/nanostores/baseUrl';
import { $customNavComponent } from 'app/store/nanostores/customNavComponent';
import type { CustomStarUi } from 'app/store/nanostores/customStarUI';
import { $customStarUI } from 'app/store/nanostores/customStarUI';
import { $isDebugging } from 'app/store/nanostores/isDebugging';
import { $logo } from 'app/store/nanostores/logo';
@@ -20,11 +18,10 @@ import { $projectId, $projectName, $projectUrl } from 'app/store/nanostores/proj
import { $queueId, DEFAULT_QUEUE_ID } from 'app/store/nanostores/queueId';
import { $store } from 'app/store/nanostores/store';
import { $toastMap } from 'app/store/nanostores/toastMap';
import { $videoUpsellComponent } from 'app/store/nanostores/videoUpsellComponent';
import { $whatsNew } from 'app/store/nanostores/whatsNew';
import { createStore } from 'app/store/store';
import type { PartialAppConfig } from 'app/types/invokeai';
import Loading from 'common/components/Loading/Loading';
import type { WorkflowSortOption, WorkflowTagCategory } from 'features/nodes/store/workflowLibrarySlice';
import {
$workflowLibraryCategoriesOptions,
$workflowLibrarySortOptions,
@@ -33,47 +30,13 @@ import {
DEFAULT_WORKFLOW_LIBRARY_SORT_OPTIONS,
DEFAULT_WORKFLOW_LIBRARY_TAG_CATEGORIES,
} from 'features/nodes/store/workflowLibrarySlice';
import type { WorkflowCategory } from 'features/nodes/types/workflow';
import type { ToastConfig } from 'features/toast/toast';
import type { PropsWithChildren, ReactNode } from 'react';
import React, { lazy, memo, useEffect, useLayoutEffect, useState } from 'react';
import { Provider } from 'react-redux';
import { addMiddleware, resetMiddlewares } from 'redux-dynamic-middlewares';
import { $socketOptions } from 'services/events/stores';
import type { ManagerOptions, SocketOptions } from 'socket.io-client';
const App = lazy(() => import('./App'));
interface Props extends PropsWithChildren {
apiUrl?: string;
openAPISchemaUrl?: string;
token?: string;
config?: PartialAppConfig;
customNavComponent?: ReactNode;
accountSettingsLink?: string;
middleware?: Middleware[];
projectId?: string;
projectName?: string;
projectUrl?: string;
queueId?: string;
studioInitAction?: StudioInitAction;
customStarUi?: CustomStarUi;
socketOptions?: Partial<ManagerOptions & SocketOptions>;
isDebugging?: boolean;
logo?: ReactNode;
toastMap?: Record<string, ToastConfig>;
whatsNew?: ReactNode[];
workflowCategories?: WorkflowCategory[];
workflowTagCategories?: WorkflowTagCategory[];
workflowSortOptions?: WorkflowSortOption[];
loggingOverrides?: LoggingOverrides;
/**
* If provided, overrides in-app navigation to the model manager
*/
onClickGoToModelManager?: () => void;
storagePersistDebounce?: number;
}
const InvokeAIUI = ({
apiUrl,
openAPISchemaUrl,
@@ -92,6 +55,8 @@ const InvokeAIUI = ({
isDebugging = false,
logo,
toastMap,
accountTypeText,
videoUpsellComponent,
workflowCategories,
workflowTagCategories,
workflowSortOptions,
@@ -99,7 +64,7 @@ const InvokeAIUI = ({
onClickGoToModelManager,
whatsNew,
storagePersistDebounce = 300,
}: Props) => {
}: InvokeAIUIProps) => {
const [store, setStore] = useState<ReturnType<typeof createStore> | undefined>(undefined);
const [didRehydrate, setDidRehydrate] = useState(false);
@@ -180,6 +145,26 @@ const InvokeAIUI = ({
};
}, [customStarUi]);
useEffect(() => {
if (accountTypeText) {
$accountTypeText.set(accountTypeText);
}
return () => {
$accountTypeText.set('');
};
}, [accountTypeText]);
useEffect(() => {
if (videoUpsellComponent) {
$videoUpsellComponent.set(videoUpsellComponent);
}
return () => {
$videoUpsellComponent.set(undefined);
};
}, [videoUpsellComponent]);
useEffect(() => {
if (customNavComponent) {
$customNavComponent.set(customNavComponent);

View File

@@ -0,0 +1,43 @@
import type { Middleware } from '@reduxjs/toolkit';
import type { StudioInitAction } from 'app/hooks/useStudioInitAction';
import type { LoggingOverrides } from 'app/logging/logger';
import type { CustomStarUi } from 'app/store/nanostores/customStarUI';
import type { PartialAppConfig } from 'app/types/invokeai';
import type { SocketOptions } from 'dgram';
import type { WorkflowSortOption, WorkflowTagCategory } from 'features/nodes/store/workflowLibrarySlice';
import type { WorkflowCategory } from 'features/nodes/types/workflow';
import type { ToastConfig } from 'features/toast/toast';
import type { PropsWithChildren, ReactNode } from 'react';
import type { ManagerOptions } from 'socket.io-client';
export interface InvokeAIUIProps extends PropsWithChildren {
apiUrl?: string;
openAPISchemaUrl?: string;
token?: string;
config?: PartialAppConfig;
customNavComponent?: ReactNode;
accountSettingsLink?: string;
middleware?: Middleware[];
projectId?: string;
projectName?: string;
projectUrl?: string;
queueId?: string;
studioInitAction?: StudioInitAction;
customStarUi?: CustomStarUi;
socketOptions?: Partial<ManagerOptions & SocketOptions>;
isDebugging?: boolean;
logo?: ReactNode;
toastMap?: Record<string, ToastConfig>;
accountTypeText?: string;
videoUpsellComponent?: ReactNode;
whatsNew?: ReactNode[];
workflowCategories?: WorkflowCategory[];
workflowTagCategories?: WorkflowTagCategory[];
workflowSortOptions?: WorkflowSortOption[];
loggingOverrides?: LoggingOverrides;
/**
* If provided, overrides in-app navigation to the model manager
*/
onClickGoToModelManager?: () => void;
storagePersistDebounce?: number;
}

View File

@@ -42,6 +42,7 @@ type StudioDestinationAction = _StudioInitAction<
| 'canvas'
| 'workflows'
| 'upscaling'
| 'video'
| 'viewAllWorkflows'
| 'viewAllWorkflowsRecommended'
| 'viewAllStylePresets';
@@ -118,7 +119,7 @@ export const useStudioInitAction = (action?: StudioInitAction) => {
const metadata = getImageMetadataResult.value;
store.dispatch(canvasReset());
// This shows a toast
await MetadataUtils.recallAll(metadata, store);
await MetadataUtils.recallAllImageMetadata(metadata, store);
},
[store, t]
);
@@ -177,6 +178,10 @@ export const useStudioInitAction = (action?: StudioInitAction) => {
// Go to the upscaling tab
navigationApi.switchToTab('upscaling');
break;
case 'video':
// Go to the video tab
await navigationApi.focusPanel('video', LAUNCHPAD_PANEL_ID);
break;
case 'viewAllWorkflows':
// Go to the workflows tab and open the workflow library modal
navigationApi.switchToTab('workflows');

View File

@@ -26,6 +26,7 @@ export const zLogNamespace = z.enum([
'system',
'queue',
'workflows',
'video',
]);
export type LogNamespace = z.infer<typeof zLogNamespace>;

View File

@@ -1,7 +1,7 @@
import { createAction } from '@reduxjs/toolkit';
import type { AppStartListening } from 'app/store/store';
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
import { imageSelected } from 'features/gallery/store/gallerySlice';
import { selectLastSelectedItem } from 'features/gallery/store/gallerySelectors';
import { itemSelected } from 'features/gallery/store/gallerySlice';
import { imagesApi } from 'services/api/endpoints/images';
export const appStarted = createAction('app/appStarted');
@@ -18,11 +18,13 @@ export const addAppStartedListener = (startAppListening: AppStartListening) => {
const firstImageLoad = await take(imagesApi.endpoints.getImageNames.matchFulfilled);
if (firstImageLoad !== null) {
const [{ payload }] = firstImageLoad;
const selectedImage = selectLastSelectedImage(getState());
const selectedImage = selectLastSelectedItem(getState());
if (selectedImage) {
return;
}
dispatch(imageSelected(payload.image_names.at(0) ?? null));
if (payload.image_names[0]) {
dispatch(itemSelected({ type: 'image', id: payload.image_names[0] }));
}
}
},
});

View File

@@ -1,8 +1,14 @@
import { isAnyOf } from '@reduxjs/toolkit';
import type { AppStartListening } from 'app/store/store';
import { selectGetImageNamesQueryArgs, selectSelectedBoardId } from 'features/gallery/store/gallerySelectors';
import { boardIdSelected, galleryViewChanged, imageSelected } from 'features/gallery/store/gallerySlice';
import {
selectGalleryView,
selectGetImageNamesQueryArgs,
selectGetVideoIdsQueryArgs,
selectSelectedBoardId,
} from 'features/gallery/store/gallerySelectors';
import { boardIdSelected, galleryViewChanged, itemSelected } from 'features/gallery/store/gallerySlice';
import { imagesApi } from 'services/api/endpoints/images';
import { videosApi } from 'services/api/endpoints/videos';
export const addBoardIdSelectedListener = (startAppListening: AppStartListening) => {
startAppListening({
@@ -11,35 +17,65 @@ export const addBoardIdSelectedListener = (startAppListening: AppStartListening)
// Cancel any in-progress instances of this listener, we don't want to select an image from a previous board
cancelActiveListeners();
if (boardIdSelected.match(action) && action.payload.selectedImageName) {
// This action already has a selected image name, we trust it is valid
if (boardIdSelected.match(action) && action.payload.select) {
// This action already has a resource selection - skip the below auto-selection logic
return;
}
const state = getState();
const board_id = selectSelectedBoardId(state);
const view = selectGalleryView(state);
const queryArgs = { ...selectGetImageNamesQueryArgs(state), board_id };
if (view === 'images' || view === 'assets') {
const queryArgs = { ...selectGetImageNamesQueryArgs(state), board_id };
// wait until the board has some images - maybe it already has some from a previous fetch
// must use getState() to ensure we do not have stale state
const isSuccess = await condition(
() => imagesApi.endpoints.getImageNames.select(queryArgs)(getState()).isSuccess,
5000
);
// wait until the board has some images - maybe it already has some from a previous fetch
// must use getState() to ensure we do not have stale state
const isSuccess = await condition(
() => imagesApi.endpoints.getImageNames.select(queryArgs)(getState()).isSuccess,
5000
);
if (!isSuccess) {
dispatch(itemSelected(null));
return;
}
if (!isSuccess) {
dispatch(imageSelected(null));
return;
// the board was just changed - we can select the first image
const imageNames = imagesApi.endpoints.getImageNames.select(queryArgs)(getState()).data?.image_names;
const imageToSelect = imageNames && imageNames.length > 0 ? imageNames[0] : null;
if (imageToSelect) {
dispatch(itemSelected({ type: 'image', id: imageToSelect }));
} else {
dispatch(itemSelected(null));
}
} else {
const queryArgs = { ...selectGetVideoIdsQueryArgs(state), board_id };
// wait until the board has some images - maybe it already has some from a previous fetch
// must use getState() to ensure we do not have stale state
const isSuccess = await condition(
() => videosApi.endpoints.getVideoIds.select(queryArgs)(getState()).isSuccess,
5000
);
if (!isSuccess) {
dispatch(itemSelected(null));
return;
}
// the board was just changed - we can select the first image
const videoIds = videosApi.endpoints.getVideoIds.select(queryArgs)(getState()).data?.video_ids;
const videoToSelect = videoIds && videoIds.length > 0 ? videoIds[0] : null;
if (videoToSelect) {
dispatch(itemSelected({ type: 'video', id: videoToSelect }));
} else {
dispatch(itemSelected(null));
}
}
// the board was just changed - we can select the first image
const imageNames = imagesApi.endpoints.getImageNames.select(queryArgs)(getState()).data?.image_names;
const imageToSelect = imageNames?.at(0) ?? null;
dispatch(imageSelected(imageToSelect));
},
});
};

View File

@@ -13,12 +13,14 @@ import {
import { refImageModelChanged, selectRefImagesSlice } from 'features/controlLayers/store/refImagesSlice';
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
import { getEntityIdentifier, isFLUXReduxConfig, isIPAdapterConfig } from 'features/controlLayers/store/types';
import { zModelIdentifierField } from 'features/nodes/types/common';
import { modelSelected } from 'features/parameters/store/actions';
import {
postProcessingModelChanged,
tileControlnetModelChanged,
upscaleModelChanged,
} from 'features/parameters/store/upscaleSlice';
import { videoModelChanged } from 'features/parameters/store/videoSlice';
import {
zParameterCLIPEmbedModel,
zParameterSpandrelImageToImageModel,
@@ -41,6 +43,7 @@ import {
isRefinerMainModelModelConfig,
isSpandrelImageToImageModelConfig,
isT5EncoderModelConfig,
isVideoModelConfig,
} from 'services/api/types';
import type { JsonObject } from 'type-fest';
@@ -81,6 +84,7 @@ export const addModelsLoadedListener = (startAppListening: AppStartListening) =>
handleCLIPEmbedModels(models, state, dispatch, log);
handleFLUXVAEModels(models, state, dispatch, log);
handleFLUXReduxModels(models, state, dispatch, log);
handleVideoModels(models, state, dispatch, log);
},
});
};
@@ -193,6 +197,22 @@ const handleLoRAModels: ModelHandler = (models, state, dispatch, log) => {
});
};
const handleVideoModels: ModelHandler = (models, state, dispatch, log) => {
const videoModels = models.filter(isVideoModelConfig);
const selectedVideoModel = state.video.videoModel;
if (selectedVideoModel && videoModels.some((m) => m.key === selectedVideoModel.key)) {
return;
}
const firstModel = videoModels[0] || null;
if (firstModel) {
log.debug({ firstModel }, 'No video model selected, selecting first available video model');
dispatch(videoModelChanged({ videoModel: zModelIdentifierField.parse(firstModel) }));
return;
}
};
const handleControlAdapterModels: ModelHandler = (models, state, dispatch, log) => {
const caModels = models.filter(isControlLayerModelConfig);
selectCanvasSlice(state).controlLayers.entities.forEach((entity) => {

View File

@@ -0,0 +1,3 @@
import { atom } from 'nanostores';
export const $accountTypeText = atom<string>('');

View File

@@ -1,6 +1,11 @@
import { atom } from 'nanostores';
import { atom, computed } from 'nanostores';
/**
* The user's auth token.
*/
export const $authToken = atom<string | undefined>();
/**
* The crossOrigin value to use for all image loading. Depends on whether the user is authenticated.
*/
export const $crossOrigin = computed($authToken, (token) => (token ? 'use-credentials' : 'anonymous'));

View File

@@ -0,0 +1,4 @@
import { atom } from 'nanostores';
import type { ReactNode } from 'react';
export const $videoUpsellComponent = atom<ReactNode | undefined>(undefined);

View File

@@ -18,7 +18,8 @@ import { addModelsLoadedListener } from 'app/store/middleware/listenerMiddleware
import { addSetDefaultSettingsListener } from 'app/store/middleware/listenerMiddleware/listeners/setDefaultSettings';
import { addSocketConnectedEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketConnected';
import { deepClone } from 'common/util/deepClone';
import { keys, mergeWith, omit, pick } from 'es-toolkit/compat';
import { merge } from 'es-toolkit';
import { omit, pick } from 'es-toolkit/compat';
import { changeBoardModalSliceConfig } from 'features/changeBoardModal/store/slice';
import { canvasSettingsSliceConfig } from 'features/controlLayers/store/canvasSettingsSlice';
import { canvasSliceConfig } from 'features/controlLayers/store/canvasSlice';
@@ -33,6 +34,7 @@ import { nodesSliceConfig } from 'features/nodes/store/nodesSlice';
import { workflowLibrarySliceConfig } from 'features/nodes/store/workflowLibrarySlice';
import { workflowSettingsSliceConfig } from 'features/nodes/store/workflowSettingsSlice';
import { upscaleSliceConfig } from 'features/parameters/store/upscaleSlice';
import { videoSliceConfig } from 'features/parameters/store/videoSlice';
import { queueSliceConfig } from 'features/queue/store/queueSlice';
import { stylePresetSliceConfig } from 'features/stylePresets/store/stylePresetSlice';
import { configSliceConfig } from 'features/system/store/configSlice';
@@ -78,6 +80,7 @@ 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,
};
@@ -111,6 +114,7 @@ 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,
};
@@ -130,16 +134,14 @@ const unserialize: UnserializeFunction = (data, key) => {
const initialState = getInitialState();
const parsed = JSON.parse(data);
// strip out old keys
const stripped = pick(deepClone(parsed), keys(initialState));
/*
* Merge in initial state as default values, covering any missing keys. You might be tempted to use _.defaultsDeep,
* but that merges arrays by index and partial objects by key. Using an identity function as the customizer results
* in behaviour like defaultsDeep, but doesn't overwrite any values that are not undefined in the migrated state.
*/
const unPersistDenylisted = mergeWith(stripped, initialState, (objVal) => objVal);
// run (additive) migrations
const migrated = persistConfig.migrate(unPersistDenylisted);
// We need to inject non-persisted values from initial state into the rehydrated state. These values always are
// required to be in the state, but won't be in the persisted data. Build an object that consists of only these
// values, then merge it with the rehydrated state.
const nonPersistedSubsetOfState = pick(initialState, persistConfig.persistDenylist ?? []);
const stateToMigrate = merge(deepClone(parsed), nonPersistedSubsetOfState);
// Run migrations to bring old state up to date with the current version.
const migrated = persistConfig.migrate(stateToMigrate);
log.debug(
{

View File

@@ -80,6 +80,7 @@ export const zAppConfig = z.object({
allowClientSideUpload: z.boolean(),
allowPublishWorkflows: z.boolean(),
allowPromptExpansion: z.boolean(),
allowVideo: z.boolean(),
disabledTabs: z.array(zTabName),
disabledFeatures: z.array(zAppFeature),
disabledSDFeatures: z.array(zSDFeature),
@@ -140,8 +141,9 @@ export const getDefaultAppConfig = (): AppConfig => ({
allowClientSideUpload: false,
allowPublishWorkflows: false,
allowPromptExpansion: false,
allowVideo: false, // used to determine if video is enabled vs upsell
shouldShowCredits: false,
disabledTabs: [],
disabledTabs: ['video'], // used to determine if video functionality is visible
disabledFeatures: ['lightbox', 'faceRestore', 'batches'] satisfies AppFeature[],
disabledSDFeatures: ['variation', 'symmetry', 'hires', 'perlinNoise', 'noiseThreshold'] satisfies SDFeature[],
sd: {

View File

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

View File

@@ -6,13 +6,13 @@ import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const useDownloadImage = () => {
export const useDownloadItem = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const authToken = useStore($authToken);
const downloadImage = useCallback(
async (image_url: string, image_name: string) => {
const downloadItem = useCallback(
async (item_url: string, item_id: string) => {
try {
const requestOpts = authToken
? {
@@ -21,7 +21,7 @@ export const useDownloadImage = () => {
},
}
: {};
const blob = await fetch(image_url, requestOpts).then((resp) => resp.blob());
const blob = await fetch(item_url, requestOpts).then((resp) => resp.blob());
if (!blob) {
throw new Error('Unable to create Blob');
}
@@ -30,7 +30,7 @@ export const useDownloadImage = () => {
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = image_name;
a.download = item_id;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
@@ -47,5 +47,5 @@ export const useDownloadImage = () => {
[t, dispatch, authToken]
);
return { downloadImage };
return { downloadItem };
};

View File

@@ -1,5 +1,6 @@
import { useAppStore } from 'app/store/storeHooks';
import { useDeleteImageModalApi } from 'features/deleteImageModal/store/state';
import { useDeleteVideoModalApi } from 'features/deleteVideoModal/store/state';
import { selectSelection } from 'features/gallery/store/gallerySelectors';
import { useClearQueue } from 'features/queue/hooks/useClearQueue';
import { useDeleteCurrentQueueItem } from 'features/queue/hooks/useDeleteCurrentQueueItem';
@@ -12,6 +13,7 @@ import { getFocusedRegion } from './focus';
export const useGlobalHotkeys = () => {
const { dispatch, getState } = useAppStore();
const isVideoEnabled = useFeatureStatus('video');
const isModelManagerEnabled = useFeatureStatus('modelManager');
const queue = useInvoke();
@@ -92,6 +94,18 @@ export const useGlobalHotkeys = () => {
dependencies: [dispatch],
});
useRegisteredHotkeys({
id: 'selectVideoTab',
category: 'app',
callback: () => {
navigationApi.switchToTab('video');
},
options: {
enabled: isVideoEnabled,
},
dependencies: [dispatch],
});
useRegisteredHotkeys({
id: 'selectWorkflowsTab',
category: 'app',
@@ -123,6 +137,8 @@ export const useGlobalHotkeys = () => {
});
const deleteImageModalApi = useDeleteImageModalApi();
const deleteVideoModalApi = useDeleteVideoModalApi();
useRegisteredHotkeys({
id: 'deleteSelection',
category: 'gallery',
@@ -135,7 +151,13 @@ export const useGlobalHotkeys = () => {
if (!selection.length) {
return;
}
deleteImageModalApi.delete(selection);
if (selection.every(({ type }) => type === 'image')) {
deleteImageModalApi.delete(selection.map((s) => s.id));
} else if (selection.every(({ type }) => type === 'video')) {
deleteVideoModalApi.delete(selection.map((s) => s.id));
} else {
// no-op, we expect selections to always be only images or only video
}
},
dependencies: [getState, deleteImageModalApi],
});

View File

@@ -13,12 +13,18 @@ import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
import { useAddImagesToBoardMutation, useRemoveImagesFromBoardMutation } from 'services/api/endpoints/images';
import { useAddVideosToBoardMutation, useRemoveVideosFromBoardMutation } from 'services/api/endpoints/videos';
const selectImagesToChange = createSelector(
selectChangeBoardModalSlice,
(changeBoardModal) => changeBoardModal.image_names
);
const selectVideosToChange = createSelector(
selectChangeBoardModalSlice,
(changeBoardModal) => changeBoardModal.video_ids
);
const selectIsModalOpen = createSelector(
selectChangeBoardModalSlice,
(changeBoardModal) => changeBoardModal.isModalOpen
@@ -32,8 +38,11 @@ const ChangeBoardModal = () => {
const { data: boards, isFetching } = useListAllBoardsQuery({ include_archived: true });
const isModalOpen = useAppSelector(selectIsModalOpen);
const imagesToChange = useAppSelector(selectImagesToChange);
const videosToChange = useAppSelector(selectVideosToChange);
const [addImagesToBoard] = useAddImagesToBoardMutation();
const [removeImagesFromBoard] = useRemoveImagesFromBoardMutation();
const [addVideosToBoard] = useAddVideosToBoardMutation();
const [removeVideosFromBoard] = useRemoveVideosFromBoardMutation();
const { t } = useTranslation();
const options = useMemo<ComboboxOption[]>(() => {
@@ -57,20 +66,41 @@ const ChangeBoardModal = () => {
}, [dispatch]);
const handleChangeBoard = useCallback(() => {
if (!imagesToChange.length || !selectedBoardId) {
if (!selectedBoardId || (imagesToChange.length === 0 && videosToChange.length === 0)) {
return;
}
if (selectedBoardId === 'none') {
removeImagesFromBoard({ image_names: imagesToChange });
} else {
addImagesToBoard({
image_names: imagesToChange,
board_id: selectedBoardId,
});
if (imagesToChange.length) {
if (selectedBoardId === 'none') {
removeImagesFromBoard({ image_names: imagesToChange });
} else {
addImagesToBoard({
image_names: imagesToChange,
board_id: selectedBoardId,
});
}
}
if (videosToChange.length) {
if (selectedBoardId === 'none') {
removeVideosFromBoard({ video_ids: videosToChange });
} else {
addVideosToBoard({
video_ids: videosToChange,
board_id: selectedBoardId,
});
}
}
dispatch(changeBoardReset());
}, [addImagesToBoard, dispatch, imagesToChange, removeImagesFromBoard, selectedBoardId]);
}, [
addImagesToBoard,
dispatch,
imagesToChange,
videosToChange,
removeImagesFromBoard,
selectedBoardId,
addVideosToBoard,
removeVideosFromBoard,
]);
const onChange = useCallback<ComboboxOnChange>((v) => {
if (!v) {
@@ -91,9 +121,15 @@ const ChangeBoardModal = () => {
>
<Flex flexDir="column" gap={4}>
<Text>
{t('boards.movingImagesToBoard', {
count: imagesToChange.length,
})}
{imagesToChange.length > 0 &&
t('boards.movingImagesToBoard', {
count: imagesToChange.length,
})}
{videosToChange.length > 0 &&
t('boards.movingVideosToBoard', {
count: videosToChange.length,
})}
:
</Text>
<FormControl isDisabled={isFetching}>
<Combobox

View File

@@ -7,6 +7,7 @@ import z from 'zod';
const zChangeBoardModalState = z.object({
isModalOpen: z.boolean().default(false),
image_names: z.array(z.string()).default(() => []),
video_ids: z.array(z.string()).default(() => []),
});
type ChangeBoardModalState = z.infer<typeof zChangeBoardModalState>;
@@ -22,6 +23,9 @@ const slice = createSlice({
imagesToChangeSelected: (state, action: PayloadAction<string[]>) => {
state.image_names = action.payload;
},
videosToChangeSelected: (state, action: PayloadAction<string[]>) => {
state.video_ids = action.payload;
},
changeBoardReset: (state) => {
state.image_names = [];
state.isModalOpen = false;
@@ -29,7 +33,7 @@ const slice = createSlice({
},
});
export const { isModalOpenChanged, imagesToChangeSelected, changeBoardReset } = slice.actions;
export const { isModalOpenChanged, imagesToChangeSelected, videosToChangeSelected, changeBoardReset } = slice.actions;
export const selectChangeBoardModalSlice = (state: RootState) => state.changeBoardModal;

View File

@@ -1,195 +0,0 @@
import {
CompositeSlider,
FormControl,
IconButton,
NumberInput,
NumberInputField,
Popover,
PopoverAnchor,
PopoverArrow,
PopoverBody,
PopoverContent,
PopoverTrigger,
} from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { clamp } from 'es-toolkit/compat';
import { useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { selectCanvasSettingsSlice, settingsBrushWidthChanged } from 'features/controlLayers/store/canvasSettingsSlice';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import type { KeyboardEvent } from 'react';
import { memo, useCallback, useEffect, useState } from 'react';
import { PiCaretDownBold } from 'react-icons/pi';
const selectBrushWidth = createSelector(selectCanvasSettingsSlice, (settings) => settings.brushWidth);
const formatPx = (v: number | string) => `${v} px`;
function mapSliderValueToRawValue(value: number) {
if (value <= 40) {
// 0 to 40 on the slider -> 1px to 50px
return 1 + (49 * value) / 40;
} else if (value <= 70) {
// 40 to 70 on the slider -> 50px to 200px
return 50 + (150 * (value - 40)) / 30;
} else {
// 70 to 100 on the slider -> 200px to 600px
return 200 + (400 * (value - 70)) / 30;
}
}
function mapRawValueToSliderValue(value: number) {
if (value <= 50) {
// 1px to 50px -> 0 to 40 on the slider
return ((value - 1) * 40) / 49;
} else if (value <= 200) {
// 50px to 200px -> 40 to 70 on the slider
return 40 + ((value - 50) * 30) / 150;
} else {
// 200px to 600px -> 70 to 100 on the slider
return 70 + ((value - 200) * 30) / 400;
}
}
function formatSliderValue(value: number) {
return `${String(mapSliderValueToRawValue(value))} px`;
}
const marks = [
mapRawValueToSliderValue(1),
mapRawValueToSliderValue(50),
mapRawValueToSliderValue(200),
mapRawValueToSliderValue(600),
];
const sliderDefaultValue = mapRawValueToSliderValue(50);
export const ToolBrushWidth = memo(() => {
const dispatch = useAppDispatch();
const isSelected = useToolIsSelected('brush');
const width = useAppSelector(selectBrushWidth);
const [localValue, setLocalValue] = useState(width);
const onChange = useCallback(
(v: number) => {
dispatch(settingsBrushWidthChanged(clamp(Math.round(v), 1, 600)));
},
[dispatch]
);
const increment = useCallback(() => {
let newWidth = Math.round(width * 1.15);
if (newWidth === width) {
newWidth += 1;
}
onChange(newWidth);
}, [onChange, width]);
const decrement = useCallback(() => {
let newWidth = Math.round(width * 0.85);
if (newWidth === width) {
newWidth -= 1;
}
onChange(newWidth);
}, [onChange, width]);
const onChangeSlider = useCallback(
(value: number) => {
onChange(mapSliderValueToRawValue(value));
},
[onChange]
);
const onBlur = useCallback(() => {
if (isNaN(Number(localValue))) {
onChange(50);
setLocalValue(50);
} else {
onChange(localValue);
}
}, [localValue, onChange]);
const onChangeNumberInput = useCallback((valueAsString: string, valueAsNumber: number) => {
setLocalValue(valueAsNumber);
}, []);
const onKeyDown = useCallback(
(e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
onBlur();
}
},
[onBlur]
);
useEffect(() => {
setLocalValue(width);
}, [width]);
useRegisteredHotkeys({
id: 'decrementToolWidth',
category: 'canvas',
callback: decrement,
options: { enabled: isSelected },
dependencies: [decrement, isSelected],
});
useRegisteredHotkeys({
id: 'incrementToolWidth',
category: 'canvas',
callback: increment,
options: { enabled: isSelected },
dependencies: [increment, isSelected],
});
return (
<Popover>
<FormControl w="min-content" gap={2}>
<PopoverAnchor>
<NumberInput
variant="outline"
display="flex"
alignItems="center"
min={1}
max={600}
value={localValue}
onChange={onChangeNumberInput}
onBlur={onBlur}
w="76px"
format={formatPx}
defaultValue={50}
onKeyDown={onKeyDown}
clampValueOnBlur={false}
>
<NumberInputField _focusVisible={{ zIndex: 0 }} title="" paddingInlineEnd={7} />
<PopoverTrigger>
<IconButton
aria-label="open-slider"
icon={<PiCaretDownBold />}
size="sm"
variant="link"
position="absolute"
insetInlineEnd={0}
h="full"
/>
</PopoverTrigger>
</NumberInput>
</PopoverAnchor>
</FormControl>
<PopoverContent w={200} pt={0} pb={2} px={4}>
<PopoverArrow />
<PopoverBody>
<CompositeSlider
min={0}
max={100}
value={mapRawValueToSliderValue(localValue)}
onChange={onChangeSlider}
defaultValue={sliderDefaultValue}
marks={marks}
formatValue={formatSliderValue}
alwaysShowMarks
/>
</PopoverBody>
</PopoverContent>
</Popover>
);
});
ToolBrushWidth.displayName = 'ToolBrushWidth';

View File

@@ -1,198 +0,0 @@
import {
CompositeSlider,
FormControl,
IconButton,
NumberInput,
NumberInputField,
Popover,
PopoverAnchor,
PopoverArrow,
PopoverBody,
PopoverContent,
PopoverTrigger,
} from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { clamp } from 'es-toolkit/compat';
import { useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import {
selectCanvasSettingsSlice,
settingsEraserWidthChanged,
} from 'features/controlLayers/store/canvasSettingsSlice';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import type { KeyboardEvent } from 'react';
import { memo, useCallback, useEffect, useState } from 'react';
import { PiCaretDownBold } from 'react-icons/pi';
const selectEraserWidth = createSelector(selectCanvasSettingsSlice, (settings) => settings.eraserWidth);
const formatPx = (v: number | string) => `${v} px`;
function mapSliderValueToRawValue(value: number) {
if (value <= 40) {
// 0 to 40 on the slider -> 1px to 50px
return 1 + (49 * value) / 40;
} else if (value <= 70) {
// 40 to 70 on the slider -> 50px to 200px
return 50 + (150 * (value - 40)) / 30;
} else {
// 70 to 100 on the slider -> 200px to 600px
return 200 + (400 * (value - 70)) / 30;
}
}
function mapRawValueToSliderValue(value: number) {
if (value <= 50) {
// 1px to 50px -> 0 to 40 on the slider
return ((value - 1) * 40) / 49;
} else if (value <= 200) {
// 50px to 200px -> 40 to 70 on the slider
return 40 + ((value - 50) * 30) / 150;
} else {
// 200px to 600px -> 70 to 100 on the slider
return 70 + ((value - 200) * 30) / 400;
}
}
function formatSliderValue(value: number) {
return `${String(mapSliderValueToRawValue(value))} px`;
}
const marks = [
mapRawValueToSliderValue(1),
mapRawValueToSliderValue(50),
mapRawValueToSliderValue(200),
mapRawValueToSliderValue(600),
];
const sliderDefaultValue = mapRawValueToSliderValue(50);
export const ToolEraserWidth = memo(() => {
const dispatch = useAppDispatch();
const isSelected = useToolIsSelected('eraser');
const width = useAppSelector(selectEraserWidth);
const [localValue, setLocalValue] = useState(width);
const onChange = useCallback(
(v: number) => {
dispatch(settingsEraserWidthChanged(clamp(Math.round(v), 1, 600)));
},
[dispatch]
);
const increment = useCallback(() => {
let newWidth = Math.round(width * 1.15);
if (newWidth === width) {
newWidth += 1;
}
onChange(newWidth);
}, [onChange, width]);
const decrement = useCallback(() => {
let newWidth = Math.round(width * 0.85);
if (newWidth === width) {
newWidth -= 1;
}
onChange(newWidth);
}, [onChange, width]);
const onChangeSlider = useCallback(
(value: number) => {
onChange(mapSliderValueToRawValue(value));
},
[onChange]
);
const onBlur = useCallback(() => {
if (isNaN(Number(localValue))) {
onChange(50);
setLocalValue(50);
} else {
onChange(localValue);
}
}, [localValue, onChange]);
const onChangeNumberInput = useCallback((valueAsString: string, valueAsNumber: number) => {
setLocalValue(valueAsNumber);
}, []);
const onKeyDown = useCallback(
(e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
onBlur();
}
},
[onBlur]
);
useEffect(() => {
setLocalValue(width);
}, [width]);
useRegisteredHotkeys({
id: 'decrementToolWidth',
category: 'canvas',
callback: decrement,
options: { enabled: isSelected },
dependencies: [decrement, isSelected],
});
useRegisteredHotkeys({
id: 'incrementToolWidth',
category: 'canvas',
callback: increment,
options: { enabled: isSelected },
dependencies: [increment, isSelected],
});
return (
<Popover>
<FormControl w="min-content" gap={2}>
<PopoverAnchor>
<NumberInput
variant="outline"
display="flex"
alignItems="center"
min={1}
max={600}
value={localValue}
onChange={onChangeNumberInput}
onBlur={onBlur}
w="76px"
format={formatPx}
defaultValue={50}
onKeyDown={onKeyDown}
clampValueOnBlur={false}
>
<NumberInputField _focusVisible={{ zIndex: 0 }} title="" paddingInlineEnd={7} />
<PopoverTrigger>
<IconButton
aria-label="open-slider"
icon={<PiCaretDownBold />}
size="sm"
variant="link"
position="absolute"
insetInlineEnd={0}
h="full"
/>
</PopoverTrigger>
</NumberInput>
</PopoverAnchor>
</FormControl>
<PopoverContent w={200} pt={0} pb={2} px={4}>
<PopoverArrow />
<PopoverBody>
<CompositeSlider
min={0}
max={100}
value={mapRawValueToSliderValue(localValue)}
onChange={onChangeSlider}
defaultValue={sliderDefaultValue}
marks={marks}
formatValue={formatSliderValue}
alwaysShowMarks
/>
</PopoverBody>
</PopoverContent>
</Popover>
);
});
ToolEraserWidth.displayName = 'ToolEraserWidth';

View File

@@ -12,29 +12,58 @@ import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import RgbaColorPicker from 'common/components/ColorPicker/RgbaColorPicker';
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
import { selectCanvasSettingsSlice, settingsColorChanged } from 'features/controlLayers/store/canvasSettingsSlice';
import {
selectCanvasSettingsSlice,
settingsActiveColorToggled,
settingsBgColorChanged,
settingsColorsSetToDefault,
settingsFgColorChanged,
} from 'features/controlLayers/store/canvasSettingsSlice';
import type { RgbaColor } from 'features/controlLayers/store/types';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { memo, useCallback } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const selectColor = createSelector(selectCanvasSettingsSlice, (settings) => settings.color);
const selectActiveColor = createSelector(selectCanvasSettingsSlice, (settings) => settings.activeColor);
const selectBgColor = createSelector(selectCanvasSettingsSlice, (settings) => settings.bgColor);
const selectFgColor = createSelector(selectCanvasSettingsSlice, (settings) => settings.fgColor);
export const ToolColorPicker = memo(() => {
export const ToolFillColorPicker = memo(() => {
const { t } = useTranslation();
const fill = useAppSelector(selectColor);
const activeColorType = useAppSelector(selectActiveColor);
const bgColor = useAppSelector(selectBgColor);
const fgColor = useAppSelector(selectFgColor);
const { activeColor, tooltip, bgColorzIndex, fgColorzIndex } = useMemo(() => {
if (activeColorType === 'bgColor') {
return { activeColor: bgColor, tooltip: t('controlLayers.fill.bgFillColor'), bgColorzIndex: 2, fgColorzIndex: 1 };
} else {
return { activeColor: fgColor, tooltip: t('controlLayers.fill.fgFillColor'), bgColorzIndex: 1, fgColorzIndex: 2 };
}
}, [activeColorType, bgColor, fgColor, t]);
const dispatch = useAppDispatch();
const onChange = useCallback(
const onColorChange = useCallback(
(color: RgbaColor) => {
dispatch(settingsColorChanged(color));
if (activeColorType === 'bgColor') {
dispatch(settingsBgColorChanged(color));
} else {
dispatch(settingsFgColorChanged(color));
}
},
[dispatch]
[activeColorType, dispatch]
);
useRegisteredHotkeys({
id: 'setFillToWhite',
id: 'setFillColorsToDefault',
category: 'canvas',
callback: () => dispatch(settingsColorChanged({ r: 255, g: 255, b: 255, a: 1 })),
callback: () => dispatch(settingsColorsSetToDefault()),
options: { preventDefault: true },
dependencies: [dispatch],
});
useRegisteredHotkeys({
id: 'toggleFillColor',
category: 'canvas',
callback: () => dispatch(settingsActiveColorToggled()),
options: { preventDefault: true },
dependencies: [dispatch],
});
@@ -42,16 +71,32 @@ export const ToolColorPicker = memo(() => {
return (
<Popover isLazy>
<PopoverTrigger>
<Flex role="button" aria-label={t('controlLayers.fill.fillColor')} tabIndex={-1} w={8} h={8}>
<Tooltip label={t('controlLayers.fill.fillColor')}>
<Flex w="full" h="full" alignItems="center" justifyContent="center">
<Flex role="button" aria-label={t('controlLayers.fill.fillColor')} tabIndex={-1} minW={8} w={8} h={8}>
<Tooltip label={tooltip}>
<Flex alignItems="center" justifyContent="center" position="relative" w="full" h="full">
<Box
borderRadius="full"
borderColor="base.600"
w={6}
h={6}
borderWidth={2}
bg={rgbaColorToString(fill)}
bg={rgbaColorToString(bgColor)}
position="absolute"
top="0"
left="0"
zIndex={bgColorzIndex}
/>
<Box
borderRadius="full"
borderColor="base.600"
w={6}
h={6}
borderWidth={2}
bg={rgbaColorToString(fgColor)}
position="absolute"
top="2"
left="2"
zIndex={fgColorzIndex}
/>
</Flex>
</Tooltip>
@@ -60,11 +105,11 @@ export const ToolColorPicker = memo(() => {
<PopoverContent>
<PopoverArrow />
<PopoverBody minH={64}>
<RgbaColorPicker color={fill} onChange={onChange} withNumberInput withSwatches />
<RgbaColorPicker color={activeColor} onChange={onColorChange} withNumberInput withSwatches />
</PopoverBody>
</PopoverContent>
</Popover>
);
});
ToolColorPicker.displayName = 'ToolFillColorPicker';
ToolFillColorPicker.displayName = 'ToolFillColorPicker';

View File

@@ -1,19 +0,0 @@
import { useStore } from '@nanostores/react';
import { ToolBrushWidth } from 'features/controlLayers/components/Tool/ToolBrushWidth';
import { ToolEraserWidth } from 'features/controlLayers/components/Tool/ToolEraserWidth';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { memo } from 'react';
export const ToolSettings = memo(() => {
const canvasManager = useCanvasManager();
const tool = useStore(canvasManager.tool.$tool);
if (tool === 'brush') {
return <ToolBrushWidth />;
}
if (tool === 'eraser') {
return <ToolEraserWidth />;
}
return null;
});
ToolSettings.displayName = 'ToolSettings';

View File

@@ -0,0 +1,332 @@
import {
CompositeNumberInput,
CompositeSlider,
Flex,
FormControl,
IconButton,
NumberInput,
NumberInputField,
Popover,
PopoverAnchor,
PopoverArrow,
PopoverBody,
PopoverContent,
PopoverTrigger,
} from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { clamp } from 'es-toolkit/compat';
import {
selectCanvasSettingsSlice,
settingsBrushWidthChanged,
settingsEraserWidthChanged,
} from 'features/controlLayers/store/canvasSettingsSlice';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import type { KeyboardEvent } from 'react';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { PiCaretDownBold } from 'react-icons/pi';
import { useToolIsSelected } from './hooks';
const formatPx = (v: number | string) => `${v} px`;
function mapSliderValueToRawValue(value: number) {
if (value <= 40) {
// 0 to 40 on the slider -> 1px to 50px
return 1 + (49 * value) / 40;
} else if (value <= 70) {
// 40 to 70 on the slider -> 50px to 200px
return 50 + (150 * (value - 40)) / 30;
} else {
// 70 to 100 on the slider -> 200px to 600px
return 200 + (400 * (value - 70)) / 30;
}
}
function mapRawValueToSliderValue(value: number) {
if (value <= 50) {
// 1px to 50px -> 0 to 40 on the slider
return ((value - 1) * 40) / 49;
} else if (value <= 200) {
// 50px to 200px -> 40 to 70 on the slider
return 40 + ((value - 50) * 30) / 150;
} else {
// 200px to 600px -> 70 to 100 on the slider
return 70 + ((value - 200) * 30) / 400;
}
}
function formatSliderValue(value: number) {
return `${String(mapSliderValueToRawValue(value))} px`;
}
const marks = [
mapRawValueToSliderValue(1),
mapRawValueToSliderValue(50),
mapRawValueToSliderValue(200),
mapRawValueToSliderValue(600),
];
const sliderDefaultValue = mapRawValueToSliderValue(50);
const SLIDER_VS_DROPDOWN_CONTAINER_WIDTH_THRESHOLD = 280;
interface ToolWidthPickerComponentProps {
localValue: number;
onChangeSlider: (value: number) => void;
onChangeInput: (value: number) => void;
onBlur: () => void;
onKeyDown: (value: KeyboardEvent<HTMLInputElement>) => void;
}
const DropDownToolWidthPickerComponent = memo(
({ localValue, onChangeSlider, onChangeInput, onKeyDown, onBlur }: ToolWidthPickerComponentProps) => {
const onChangeNumberInput = useCallback(
(valueAsString: string, valueAsNumber: number) => {
onChangeInput(valueAsNumber);
},
[onChangeInput]
);
return (
<Popover>
<FormControl w="min-content" gap={2} overflow="hidden">
<PopoverAnchor>
<NumberInput
variant="outline"
display="flex"
alignItems="center"
min={1}
max={600}
value={localValue}
onChange={onChangeNumberInput}
onBlur={onBlur}
w={76}
format={formatPx}
defaultValue={50}
onKeyDown={onKeyDown}
clampValueOnBlur={false}
>
<NumberInputField _focusVisible={{ zIndex: 0 }} title="" paddingInlineEnd={7} />
<PopoverTrigger>
<IconButton
aria-label="open-slider"
icon={<PiCaretDownBold />}
size="sm"
variant="link"
position="absolute"
insetInlineEnd={0}
h="full"
/>
</PopoverTrigger>
</NumberInput>
</PopoverAnchor>
</FormControl>
<PopoverContent w={200} pt={0} pb={2} px={4}>
<PopoverArrow />
<PopoverBody>
<CompositeSlider
min={0}
max={100}
value={mapRawValueToSliderValue(localValue)}
onChange={onChangeSlider}
defaultValue={sliderDefaultValue}
marks={marks}
formatValue={formatSliderValue}
alwaysShowMarks
/>
</PopoverBody>
</PopoverContent>
</Popover>
);
}
);
DropDownToolWidthPickerComponent.displayName = 'DropDownToolWidthPickerComponent';
const SliderToolWidthPickerComponent = memo(
({ localValue, onChangeSlider, onChangeInput, onKeyDown, onBlur }: ToolWidthPickerComponentProps) => {
return (
<Flex w={SLIDER_VS_DROPDOWN_CONTAINER_WIDTH_THRESHOLD} gap={4}>
<CompositeSlider
w={200}
h="unset"
min={0}
max={100}
value={mapRawValueToSliderValue(localValue)}
onChange={onChangeSlider}
defaultValue={sliderDefaultValue}
marks={marks}
formatValue={formatSliderValue}
alwaysShowMarks
/>
<CompositeNumberInput
w={28}
variant="outline"
min={1}
max={600}
value={localValue}
onChange={onChangeInput}
onBlur={onBlur}
onKeyDown={onKeyDown}
format={formatPx}
defaultValue={50}
/>
</Flex>
);
}
);
SliderToolWidthPickerComponent.displayName = 'SliderToolWidthPickerComponent';
const selectBrushWidth = createSelector(selectCanvasSettingsSlice, (settings) => settings.brushWidth);
const selectEraserWidth = createSelector(selectCanvasSettingsSlice, (settings) => settings.eraserWidth);
export const ToolWidthPicker = memo(() => {
const ref = useRef<HTMLDivElement>(null);
const dispatch = useAppDispatch();
const isBrushSelected = useToolIsSelected('brush');
const isEraserSelected = useToolIsSelected('eraser');
const isToolSelected = useMemo(() => {
return isBrushSelected || isEraserSelected;
}, [isBrushSelected, isEraserSelected]);
const brushWidth = useAppSelector(selectBrushWidth);
const eraserWidth = useAppSelector(selectEraserWidth);
const width = useMemo(() => {
if (isBrushSelected) {
return brushWidth;
}
if (isEraserSelected) {
return eraserWidth;
}
return 0;
}, [isBrushSelected, isEraserSelected, brushWidth, eraserWidth]);
const [localValue, setLocalValue] = useState(width);
const [componentType, setComponentType] = useState<'slider' | 'dropdown' | null>(null);
useEffect(() => {
const el = ref.current;
if (!el) {
return;
}
const observer = new ResizeObserver((entries) => {
for (let entry of entries) {
if (entry.contentRect.width > SLIDER_VS_DROPDOWN_CONTAINER_WIDTH_THRESHOLD) {
setComponentType('slider');
} else {
setComponentType('dropdown');
}
}
});
observer.observe(el);
return () => {
observer.disconnect();
};
}, []);
const onValueChange = useCallback(
(value: number) => {
if (isBrushSelected) {
dispatch(settingsBrushWidthChanged(value));
} else if (isEraserSelected) {
dispatch(settingsEraserWidthChanged(value));
}
},
[isBrushSelected, isEraserSelected, dispatch]
);
const onChange = useCallback(
(value: number) => {
onValueChange(clamp(Math.round(value), 1, 600));
},
[onValueChange]
);
const increment = useCallback(() => {
let newWidth = Math.round(width * 1.15);
if (newWidth === width) {
newWidth += 1;
}
onChange(newWidth);
}, [onChange, width]);
const decrement = useCallback(() => {
let newWidth = Math.round(width * 0.85);
if (newWidth === width) {
newWidth -= 1;
}
onChange(newWidth);
}, [onChange, width]);
const onChangeSlider = useCallback(
(value: number) => {
onChange(mapSliderValueToRawValue(value));
},
[onChange]
);
const onChangeInput = useCallback((value: number) => {
setLocalValue(value);
}, []);
const onBlur = useCallback(() => {
if (isNaN(Number(localValue))) {
onChange(50);
setLocalValue(50);
} else {
onChange(localValue);
}
}, [localValue, onChange]);
const onKeyDown = useCallback(
(e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
onBlur();
}
},
[onBlur]
);
useEffect(() => {
setLocalValue(width);
}, [width]);
useRegisteredHotkeys({
id: 'decrementToolWidth',
category: 'canvas',
callback: decrement,
options: { enabled: isToolSelected },
dependencies: [decrement, isToolSelected],
});
useRegisteredHotkeys({
id: 'incrementToolWidth',
category: 'canvas',
callback: increment,
options: { enabled: isToolSelected },
dependencies: [increment, isToolSelected],
});
return (
<Flex ref={ref} alignItems="center" h="full" flexGrow={1} flexShrink={1} justifyContent="flex-start" px={4}>
{componentType === 'slider' && (
<SliderToolWidthPickerComponent
localValue={localValue}
onChangeSlider={onChangeSlider}
onChangeInput={onChangeInput}
onBlur={onBlur}
onKeyDown={onKeyDown}
/>
)}
{componentType === 'dropdown' && (
<DropDownToolWidthPickerComponent
localValue={localValue}
onChangeSlider={onChangeSlider}
onChangeInput={onChangeInput}
onBlur={onBlur}
onKeyDown={onKeyDown}
/>
)}
</Flex>
);
});
ToolWidthPicker.displayName = 'ToolWidthPicker';

View File

@@ -1,7 +1,8 @@
import { Divider, Flex } from '@invoke-ai/ui-library';
import { CanvasSettingsPopover } from 'features/controlLayers/components/Settings/CanvasSettingsPopover';
import { ToolColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
import { ToolSettings } from 'features/controlLayers/components/Tool/ToolSettings';
import { useToolIsSelected } from 'features/controlLayers/components/Tool/hooks';
import { ToolFillColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
import { ToolWidthPicker } from 'features/controlLayers/components/Tool/ToolWidthPicker';
import { CanvasToolbarFitBboxToLayersButton } from 'features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton';
import { CanvasToolbarFitBboxToMasksButton } from 'features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToMasksButton';
import { CanvasToolbarNewSessionMenuButton } from 'features/controlLayers/components/Toolbar/CanvasToolbarNewSessionMenuButton';
@@ -20,9 +21,15 @@ import { useCanvasToggleNonRasterLayersHotkey } from 'features/controlLayers/hoo
import { useCanvasTransformHotkey } from 'features/controlLayers/hooks/useCanvasTransformHotkey';
import { useCanvasUndoRedoHotkeys } from 'features/controlLayers/hooks/useCanvasUndoRedoHotkeys';
import { useNextPrevEntityHotkeys } from 'features/controlLayers/hooks/useNextPrevEntity';
import { memo } from 'react';
import { memo, useMemo } from 'react';
export const CanvasToolbar = memo(() => {
const isBrushSelected = useToolIsSelected('brush');
const isEraserSelected = useToolIsSelected('eraser');
const showToolWithPicker = useMemo(() => {
return isBrushSelected || isEraserSelected;
}, [isBrushSelected, isEraserSelected]);
useCanvasResetLayerHotkey();
useCanvasDeleteLayerHotkey();
useCanvasUndoRedoHotkeys();
@@ -36,9 +43,11 @@ export const CanvasToolbar = memo(() => {
return (
<Flex w="full" gap={2} alignItems="center" px={2}>
<ToolColorPicker />
<ToolSettings />
<Flex alignItems="center" h="full" flexGrow={1} justifyContent="flex-end">
<Flex alignItems="center" h="full" flexGrow={1}>
<ToolFillColorPicker />
{showToolWithPicker && <ToolWidthPicker />}
</Flex>
<Flex alignItems="center" h="full">
<CanvasToolbarScale />
<CanvasToolbarResetViewButton />
<CanvasToolbarFitBboxToLayersButton />

View File

@@ -216,11 +216,8 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
// We should also never cache if the entity has no dimensions. Konva will log an error to console like this:
// Konva error: Can not cache the node. Width or height of the node equals 0. Caching is skipped.
//
// It won't raise - just logs the error.
const hasContent = this.konva.objectGroup.width() > 0 && this.konva.objectGroup.height() > 0;
if (hasContent && isVisible && (force || !isCached)) {
if (isVisible && (force || !isCached)) {
this.log.trace('Caching object group');
this.konva.objectGroup.clearCache();
this.konva.objectGroup.cache({ pixelRatio: 1, imageSmoothingEnabled: false });

View File

@@ -41,7 +41,7 @@ export class CanvasObjectImage extends CanvasModuleBase {
state: CanvasImageState;
config: CanvasObjectImageConfig;
config: CanvasObjectImageConfig = DEFAULT_CONFIG;
konva: {
group: Konva.Group;
@@ -60,8 +60,7 @@ export class CanvasObjectImage extends CanvasModuleBase {
| CanvasEntityBufferObjectRenderer
| CanvasStagingAreaModule
| CanvasSegmentAnythingModule
| CanvasEntityFilterer,
config = DEFAULT_CONFIG
| CanvasEntityFilterer
) {
super();
this.id = state.id;
@@ -69,7 +68,6 @@ export class CanvasObjectImage extends CanvasModuleBase {
this.manager = parent.manager;
this.path = this.manager.buildPath(this);
this.log = this.manager.buildLogger(this);
this.config = config;
this.log.debug({ state }, 'Creating module');

View File

@@ -125,9 +125,14 @@ export class CanvasStageModule extends CanvasModuleBase {
this.konva.stage.on('dragmove', this.onStageDragMove);
this.konva.stage.on('dragend', this.onStageDragEnd);
// Start dragging the stage when the middle mouse button is clicked. We do not need to listen for 'pointerdown' to
// do cleanup - that is done in onStageDragEnd.
// Start dragging the stage when the middle mouse button is clicked and stop dragging when it's released.
// We _also_ stop dragging on dragend - but in case the user doesn't actually start a drag (just clicks MMB once),
// we still need to stop dragging.
//
// Why start dragging on pointerdown instead of dragstart? Because it allows us to immediately show the cursor as
// grabbing, instead of waiting for the user to actually move the mouse to start the drag. Minor UX affordance.
this.konva.stage.on('pointerdown', this.onStagePointerDown);
this.konva.stage.on('pointerup', this.onStagePointerUp);
this.subscriptions.add(() => this.konva.stage.off('wheel', this.onStageMouseWheel));
this.subscriptions.add(() => this.konva.stage.off('dragmove', this.onStageDragMove));
@@ -438,6 +443,13 @@ export class CanvasStageModule extends CanvasModuleBase {
}
};
onStagePointerUp = (e: KonvaEventObject<PointerEvent>) => {
// If the middle mouse button is released and we are dragging, stop dragging the stage
if (e.evt.button === 1) {
this.stopDragging();
}
};
/**
* Forcibly starts dragging the stage. This is useful when you want to start dragging the stage programmatically.
*/

View File

@@ -229,17 +229,25 @@ export class CanvasStagingAreaModule extends CanvasModuleBase {
if (imageSrc) {
const image = this._getImageFromSrc(imageSrc, width, height);
// Some models do not make guarantees about their output dimensions. This flag allows the staged images to
// render at their real dimensions, instead of the bbox size.
//
// When the image source is an image name, it is a final output image. In that case, we should use its
// physical dimensions. Otherwise, if it is a dataURL, that means it is a progress image. These come in at
// a smaller resolution and need to be stretched to fill the bbox, so we do not use the physical
// dimensions in that case.
const usePhysicalDimensions = imageSrc.type === 'imageName';
if (!this.image) {
this.image = new CanvasObjectImage({ id: 'staging-area-image', type: 'image', image }, this, {
// Some models do not make guarantees about their output dimensions. This flag allows the staged images to
// render at their real dimensions, instead of the bbox size.
usePhysicalDimensions: true,
});
this.image = new CanvasObjectImage({ id: 'staging-area-image', type: 'image', image }, this);
this.image.config.usePhysicalDimensions = usePhysicalDimensions;
await this.image.update(this.image.state, true);
this.konva.group.add(this.image.konva.group);
} else if (this.image.isLoading || this.image.isError) {
// noop
} else {
this.image.config.usePhysicalDimensions = usePhysicalDimensions;
await this.image.update({ ...this.image.state, image });
}
this.konva.placeholder.group.visible(false);

View File

@@ -10,9 +10,10 @@ import type { SubscriptionHandler } from 'features/controlLayers/konva/util';
import { createReduxSubscription, getPrefixedId } from 'features/controlLayers/konva/util';
import {
selectCanvasSettingsSlice,
settingsBgColorChanged,
settingsBrushWidthChanged,
settingsColorChanged,
settingsEraserWidthChanged,
settingsFgColorChanged,
} from 'features/controlLayers/store/canvasSettingsSlice';
import {
bboxChangedFromCanvas,
@@ -232,7 +233,9 @@ export class CanvasStateApiModule extends CanvasModuleBase {
* Sets the drawing color, pushing state to redux.
*/
setColor = (color: Partial<RgbaColor>) => {
return this.store.dispatch(settingsColorChanged(color));
return this.getSettings().activeColor === 'bgColor'
? this.store.dispatch(settingsBgColorChanged(color))
: this.store.dispatch(settingsFgColorChanged(color));
};
/**
@@ -421,7 +424,8 @@ export class CanvasStateApiModule extends CanvasModuleBase {
* consistency with conventional black and white mask images, we use black as the color for these entities.
*/
getCurrentColor = (): RgbaColor => {
let color: RgbaColor = this.getSettings().color;
let color: RgbaColor =
this.getSettings().activeColor === 'bgColor' ? this.getSettings().bgColor : this.getSettings().fgColor;
const selectedEntity = this.getSelectedEntityAdapter();
if (selectedEntity) {
// These two entity types use a compositing rect for opacity. Their fill is always a solid color.
@@ -449,7 +453,7 @@ export class CanvasStateApiModule extends CanvasModuleBase {
// selected entity's fill color with 50% opacity.
return { ...selectedEntity.state.fill.color, a: 0.5 };
} else {
return this.getSettings().color;
return this.getSettings().activeColor === 'bgColor' ? this.getSettings().bgColor : this.getSettings().fgColor;
}
};

View File

@@ -328,6 +328,7 @@ export class CanvasColorPickerToolModule extends CanvasModuleBase {
const colorPickerOuterRadius = this.manager.stage.unscale(this.config.RING_OUTER_RADIUS);
const onePixel = this.manager.stage.unscale(1);
const twoPixels = this.manager.stage.unscale(2);
const color = settings.activeColor === 'bgColor' ? settings.bgColor : settings.fgColor;
this.konva.ringCandidateColor.setAttrs({
x,
@@ -339,7 +340,7 @@ export class CanvasColorPickerToolModule extends CanvasModuleBase {
this.konva.ringCurrentColor.setAttrs({
x,
y,
fill: rgbColorToString(settings.color),
fill: rgbColorToString(color),
innerRadius: colorPickerInnerRadius,
outerRadius: colorPickerOuterRadius,
});

View File

@@ -1,5 +1,5 @@
import type { Selector, Store } from '@reduxjs/toolkit';
import { $authToken } from 'app/store/nanostores/authToken';
import { $authToken, $crossOrigin } from 'app/store/nanostores/authToken';
import { roundDownToMultiple, roundUpToMultiple } from 'common/util/roundDownToMultiple';
import { clamp } from 'es-toolkit/compat';
import type {
@@ -494,7 +494,7 @@ export async function loadImage(src: string, fetchUrlFirst?: boolean): Promise<H
const imageElement = new Image();
imageElement.onload = () => resolve(imageElement);
imageElement.onerror = (error) => reject(error);
imageElement.crossOrigin = $authToken.get() ? 'use-credentials' : 'anonymous';
imageElement.crossOrigin = $crossOrigin.get();
imageElement.src = url;
});
}

View File

@@ -2,7 +2,8 @@ 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 { zRgbaColor } from 'features/controlLayers/store/types';
import type { RgbaColor } from 'features/controlLayers/store/types';
import { RGBA_BLACK, RGBA_WHITE, zRgbaColor } from 'features/controlLayers/store/types';
import { z } from 'zod';
const zAutoSwitchMode = z.enum(['off', 'switch_on_start', 'switch_on_finish']);
@@ -35,9 +36,11 @@ const zCanvasSettingsState = z.object({
*/
eraserWidth: z.int().gt(0),
/**
* The color to use when drawing lines or filling shapes.
* The colors to use when drawing lines or filling shapes.
*/
color: zRgbaColor,
activeColor: z.enum(['bgColor', 'fgColor']),
bgColor: zRgbaColor,
fgColor: zRgbaColor,
/**
* Whether to composite inpainted/outpainted regions back onto the source image when saving canvas generations.
*
@@ -100,7 +103,9 @@ const getInitialState = (): CanvasSettingsState => ({
invertScrollForToolWidth: false,
brushWidth: 50,
eraserWidth: 50,
color: { r: 31, g: 160, b: 224, a: 1 }, // invokeBlue.500
activeColor: 'fgColor',
bgColor: RGBA_BLACK,
fgColor: RGBA_WHITE,
outputOnlyMaskedRegions: true,
autoProcess: true,
snapToGrid: true,
@@ -134,8 +139,18 @@ const slice = createSlice({
settingsEraserWidthChanged: (state, action: PayloadAction<CanvasSettingsState['eraserWidth']>) => {
state.eraserWidth = Math.round(action.payload);
},
settingsColorChanged: (state, action: PayloadAction<Partial<CanvasSettingsState['color']>>) => {
state.color = { ...state.color, ...action.payload };
settingsActiveColorToggled: (state) => {
state.activeColor = state.activeColor === 'bgColor' ? 'fgColor' : 'bgColor';
},
settingsBgColorChanged: (state, action: PayloadAction<Partial<RgbaColor>>) => {
state.bgColor = { ...state.bgColor, ...action.payload };
},
settingsFgColorChanged: (state, action: PayloadAction<Partial<RgbaColor>>) => {
state.fgColor = { ...state.fgColor, ...action.payload };
},
settingsColorsSetToDefault: (state) => {
state.bgColor = RGBA_BLACK;
state.fgColor = RGBA_WHITE;
},
settingsInvertScrollForToolWidthChanged: (
state,
@@ -191,7 +206,10 @@ export const {
settingsShowHUDToggled,
settingsBrushWidthChanged,
settingsEraserWidthChanged,
settingsColorChanged,
settingsActiveColorToggled,
settingsBgColorChanged,
settingsFgColorChanged,
settingsColorsSetToDefault,
settingsInvertScrollForToolWidthChanged,
settingsOutputOnlyMaskedRegionsToggled,
settingsAutoProcessToggled,

View File

@@ -1611,6 +1611,7 @@ const slice = createSlice({
state.bbox.rect.width = 1024;
state.bbox.rect.height = 1024;
}
syncScaledSize(state);
}
});

View File

@@ -4,6 +4,7 @@ import type { SliceConfig } from 'app/store/types';
import { paramsReset } from 'features/controlLayers/store/paramsSlice';
import { type LoRA, zLoRA } from 'features/controlLayers/store/types';
import { zModelIdentifierField } from 'features/nodes/types/common';
import { DEFAULT_LORA_WEIGHT_CONFIG } from 'features/system/store/configSlice';
import type { LoRAModelConfig } from 'services/api/types';
import { v4 as uuidv4 } from 'uuid';
import z from 'zod';
@@ -13,11 +14,6 @@ const zLoRAsState = z.object({
});
type LoRAsState = z.infer<typeof zLoRAsState>;
const defaultLoRAConfig: Pick<LoRA, 'weight' | 'isEnabled'> = {
weight: 0.75,
isEnabled: true,
};
const getInitialState = (): LoRAsState => ({
loras: [],
});
@@ -32,6 +28,10 @@ const slice = createSlice({
reducer: (state, action: PayloadAction<{ model: LoRAModelConfig; id: string }>) => {
const { model, id } = action.payload;
const parsedModel = zModelIdentifierField.parse(model);
const defaultLoRAConfig: Pick<LoRA, 'weight' | 'isEnabled'> = {
weight: model.default_settings?.weight ?? DEFAULT_LORA_WEIGHT_CONFIG.initial,
isEnabled: true,
};
state.loras.push({ ...defaultLoRAConfig, model: parsedModel, id });
},
prepare: (payload: { model: LoRAModelConfig }) => ({ payload: { ...payload, id: uuidv4() } }),
@@ -87,3 +87,7 @@ export const lorasSliceConfig: SliceConfig<typeof slice> = {
export const selectLoRAsSlice = (state: RootState) => state.loras;
export const selectAddedLoRAs = createSelector(selectLoRAsSlice, (loras) => loras.loras);
export const buildSelectLoRA = (id: string) =>
createSelector([selectLoRAsSlice], (loras) => {
return selectLoRA(loras, id);
});

View File

@@ -4,6 +4,7 @@ import type { RootState } from 'app/store/store';
import type { SliceConfig } from 'app/store/types';
import { deepClone } from 'common/util/deepClone';
import { roundDownToMultiple, roundToMultiple } from 'common/util/roundDownToMultiple';
import { isPlainObject } from 'es-toolkit';
import { clamp } from 'es-toolkit/compat';
import type { AspectRatioID, ParamsState, RgbaColor } from 'features/controlLayers/store/types';
import {
@@ -52,6 +53,7 @@ import type {
import { getGridSize, getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { modelConfigsAdapterSelectors, selectModelConfigsQuery } from 'services/api/endpoints/models';
import { isNonRefinerMainModelConfig } from 'services/api/types';
import { assert } from 'tsafe';
const slice = createSlice({
name: 'params',
@@ -122,8 +124,8 @@ const slice = createSlice({
state.dimensions.aspectRatio.isLocked = true;
state.dimensions.aspectRatio.value = 1;
state.dimensions.aspectRatio.id = '1:1';
state.dimensions.rect.width = 1024;
state.dimensions.rect.height = 1024;
state.dimensions.width = 1024;
state.dimensions.height = 1024;
}
applyClipSkip(state, model, state.clipSkip);
@@ -247,26 +249,26 @@ const slice = createSlice({
sizeRecalled: (state, action: PayloadAction<{ width: number; height: number }>) => {
const { width, height } = action.payload;
const gridSize = getGridSize(state.model?.base);
state.dimensions.rect.width = Math.max(roundDownToMultiple(width, gridSize), 64);
state.dimensions.rect.height = Math.max(roundDownToMultiple(height, gridSize), 64);
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
state.dimensions.width = Math.max(roundDownToMultiple(width, gridSize), 64);
state.dimensions.height = Math.max(roundDownToMultiple(height, gridSize), 64);
state.dimensions.aspectRatio.value = state.dimensions.width / state.dimensions.height;
state.dimensions.aspectRatio.id = 'Free';
state.dimensions.aspectRatio.isLocked = true;
},
widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean; clamp?: boolean }>) => {
const { width, updateAspectRatio, clamp } = action.payload;
const gridSize = getGridSize(state.model?.base);
state.dimensions.rect.width = clamp ? Math.max(roundDownToMultiple(width, gridSize), 64) : width;
state.dimensions.width = clamp ? Math.max(roundDownToMultiple(width, gridSize), 64) : width;
if (state.dimensions.aspectRatio.isLocked) {
state.dimensions.rect.height = roundToMultiple(
state.dimensions.rect.width / state.dimensions.aspectRatio.value,
state.dimensions.height = roundToMultiple(
state.dimensions.width / state.dimensions.aspectRatio.value,
gridSize
);
}
if (updateAspectRatio || !state.dimensions.aspectRatio.isLocked) {
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
state.dimensions.aspectRatio.value = state.dimensions.width / state.dimensions.height;
state.dimensions.aspectRatio.id = 'Free';
state.dimensions.aspectRatio.isLocked = false;
}
@@ -274,17 +276,17 @@ const slice = createSlice({
heightChanged: (state, action: PayloadAction<{ height: number; updateAspectRatio?: boolean; clamp?: boolean }>) => {
const { height, updateAspectRatio, clamp } = action.payload;
const gridSize = getGridSize(state.model?.base);
state.dimensions.rect.height = clamp ? Math.max(roundDownToMultiple(height, gridSize), 64) : height;
state.dimensions.height = clamp ? Math.max(roundDownToMultiple(height, gridSize), 64) : height;
if (state.dimensions.aspectRatio.isLocked) {
state.dimensions.rect.width = roundToMultiple(
state.dimensions.rect.height * state.dimensions.aspectRatio.value,
state.dimensions.width = roundToMultiple(
state.dimensions.height * state.dimensions.aspectRatio.value,
gridSize
);
}
if (updateAspectRatio || !state.dimensions.aspectRatio.isLocked) {
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
state.dimensions.aspectRatio.value = state.dimensions.width / state.dimensions.height;
state.dimensions.aspectRatio.id = 'Free';
state.dimensions.aspectRatio.isLocked = false;
}
@@ -299,55 +301,55 @@ const slice = createSlice({
state.dimensions.aspectRatio.isLocked = false;
} else if ((state.model?.base === 'imagen3' || state.model?.base === 'imagen4') && isImagenAspectRatioID(id)) {
const { width, height } = IMAGEN_ASPECT_RATIOS[id];
state.dimensions.rect.width = width;
state.dimensions.rect.height = height;
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
state.dimensions.width = width;
state.dimensions.height = height;
state.dimensions.aspectRatio.value = state.dimensions.width / state.dimensions.height;
state.dimensions.aspectRatio.isLocked = true;
} else if (state.model?.base === 'chatgpt-4o' && isChatGPT4oAspectRatioID(id)) {
const { width, height } = CHATGPT_ASPECT_RATIOS[id];
state.dimensions.rect.width = width;
state.dimensions.rect.height = height;
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
state.dimensions.width = width;
state.dimensions.height = height;
state.dimensions.aspectRatio.value = state.dimensions.width / state.dimensions.height;
state.dimensions.aspectRatio.isLocked = true;
} else if (state.model?.base === 'gemini-2.5' && isGemini2_5AspectRatioID(id)) {
const { width, height } = GEMINI_2_5_ASPECT_RATIOS[id];
state.dimensions.rect.width = width;
state.dimensions.rect.height = height;
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
state.dimensions.width = width;
state.dimensions.height = height;
state.dimensions.aspectRatio.value = state.dimensions.width / state.dimensions.height;
state.dimensions.aspectRatio.isLocked = true;
} else if (state.model?.base === 'flux-kontext' && isFluxKontextAspectRatioID(id)) {
const { width, height } = FLUX_KONTEXT_ASPECT_RATIOS[id];
state.dimensions.rect.width = width;
state.dimensions.rect.height = height;
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
state.dimensions.width = width;
state.dimensions.height = height;
state.dimensions.aspectRatio.value = state.dimensions.width / state.dimensions.height;
state.dimensions.aspectRatio.isLocked = true;
} else {
state.dimensions.aspectRatio.isLocked = true;
state.dimensions.aspectRatio.value = ASPECT_RATIO_MAP[id].ratio;
const { width, height } = calculateNewSize(
state.dimensions.aspectRatio.value,
state.dimensions.rect.width * state.dimensions.rect.height,
state.dimensions.width * state.dimensions.height,
state.model?.base
);
state.dimensions.rect.width = width;
state.dimensions.rect.height = height;
state.dimensions.width = width;
state.dimensions.height = height;
}
},
dimensionsSwapped: (state) => {
state.dimensions.aspectRatio.value = 1 / state.dimensions.aspectRatio.value;
if (state.dimensions.aspectRatio.id === 'Free') {
const newWidth = state.dimensions.rect.height;
const newHeight = state.dimensions.rect.width;
state.dimensions.rect.width = newWidth;
state.dimensions.rect.height = newHeight;
const newWidth = state.dimensions.height;
const newHeight = state.dimensions.width;
state.dimensions.width = newWidth;
state.dimensions.height = newHeight;
} else {
const { width, height } = calculateNewSize(
state.dimensions.aspectRatio.value,
state.dimensions.rect.width * state.dimensions.rect.height,
state.dimensions.width * state.dimensions.height,
state.model?.base
);
state.dimensions.rect.width = width;
state.dimensions.rect.height = height;
state.dimensions.width = width;
state.dimensions.height = height;
state.dimensions.aspectRatio.id = ASPECT_RATIO_MAP[state.dimensions.aspectRatio.id].inverseID;
}
},
@@ -359,25 +361,25 @@ const slice = createSlice({
optimalDimension * optimalDimension,
state.model?.base
);
state.dimensions.rect.width = width;
state.dimensions.rect.height = height;
state.dimensions.width = width;
state.dimensions.height = height;
} else {
state.dimensions.aspectRatio = deepClone(DEFAULT_ASPECT_RATIO_CONFIG);
state.dimensions.rect.width = optimalDimension;
state.dimensions.rect.height = optimalDimension;
state.dimensions.width = optimalDimension;
state.dimensions.height = optimalDimension;
}
},
syncedToOptimalDimension: (state) => {
const optimalDimension = getOptimalDimension(state.model?.base);
if (!getIsSizeOptimal(state.dimensions.rect.width, state.dimensions.rect.height, state.model?.base)) {
if (!getIsSizeOptimal(state.dimensions.width, state.dimensions.height, state.model?.base)) {
const bboxDims = calculateNewSize(
state.dimensions.aspectRatio.value,
optimalDimension * optimalDimension,
state.model?.base
);
state.dimensions.rect.width = bboxDims.width;
state.dimensions.rect.height = bboxDims.height;
state.dimensions.width = bboxDims.width;
state.dimensions.height = bboxDims.height;
}
},
paramsReset: (state) => resetState(state),
@@ -391,7 +393,7 @@ const applyClipSkip = (state: { clipSkip: number }, model: ParameterModel | null
const maxClip = getModelMaxClipSkip(model);
state.clipSkip = clamp(clipSkip, 0, maxClip);
state.clipSkip = clamp(clipSkip, 0, maxClip ?? 0);
};
const hasModelClipSkip = (model: ParameterModel | null) => {
@@ -399,7 +401,7 @@ const hasModelClipSkip = (model: ParameterModel | null) => {
return false;
}
return getModelMaxClipSkip(model) > 0;
return getModelMaxClipSkip(model) ?? 0 > 0;
};
const getModelMaxClipSkip = (model: ParameterModel) => {
@@ -408,7 +410,7 @@ const getModelMaxClipSkip = (model: ParameterModel) => {
return 0;
}
return CLIP_SKIP_MAP[model.base].maxClip;
return CLIP_SKIP_MAP[model.base]?.maxClip;
};
const resetState = (state: ParamsState): ParamsState => {
@@ -488,7 +490,18 @@ export const paramsSliceConfig: SliceConfig<typeof slice> = {
schema: zParamsState,
getInitialState: getInitialParamsState,
persistConfig: {
migrate: (state) => zParamsState.parse(state),
migrate: (state) => {
assert(isPlainObject(state));
if (!('_version' in state)) {
// v0 -> v1, add _version and remove x/y from dimensions, lifting width/height to top level
state._version = 1;
state.dimensions.width = state.dimensions.rect.width;
state.dimensions.height = state.dimensions.rect.height;
}
return zParamsState.parse(state);
},
},
};
@@ -600,8 +613,8 @@ export const selectRefinerScheduler = createParamsSelector((params) => params.re
export const selectRefinerStart = createParamsSelector((params) => params.refinerStart);
export const selectRefinerSteps = createParamsSelector((params) => params.refinerSteps);
export const selectWidth = createParamsSelector((params) => params.dimensions.rect.width);
export const selectHeight = createParamsSelector((params) => params.dimensions.rect.height);
export const selectWidth = createParamsSelector((params) => params.dimensions.width);
export const selectHeight = createParamsSelector((params) => params.dimensions.height);
export const selectAspectRatioID = createParamsSelector((params) => params.dimensions.aspectRatio.id);
export const selectAspectRatioValue = createParamsSelector((params) => params.dimensions.aspectRatio.value);
export const selectAspectRatioIsLocked = createParamsSelector((params) => params.dimensions.aspectRatio.isLocked);

View File

@@ -82,6 +82,7 @@ export const zRgbaColor = zRgbColor.extend({
});
export type RgbaColor = z.infer<typeof zRgbaColor>;
export const RGBA_BLACK: RgbaColor = { r: 0, g: 0, b: 0, a: 1 };
export const RGBA_WHITE: RgbaColor = { r: 255, g: 255, b: 255, a: 1 };
const zOpacity = z.number().gte(0).lte(1);
@@ -482,6 +483,33 @@ export const FLUX_KONTEXT_ASPECT_RATIOS: Record<FluxKontextAspectRatio, Dimensio
'1:1': { width: 1024, height: 1024 },
};
export const zVeo3AspectRatioID = z.enum(['16:9']);
type Veo3AspectRatio = z.infer<typeof zVeo3AspectRatioID>;
export const isVeo3AspectRatioID = (v: unknown): v is Veo3AspectRatio => zVeo3AspectRatioID.safeParse(v).success;
export const zRunwayAspectRatioID = z.enum(['16:9', '4:3', '1:1', '3:4', '9:16', '21:9']);
type RunwayAspectRatio = z.infer<typeof zRunwayAspectRatioID>;
export const isRunwayAspectRatioID = (v: unknown): v is RunwayAspectRatio => zRunwayAspectRatioID.safeParse(v).success;
export const zVideoAspectRatio = z.union([zVeo3AspectRatioID, zRunwayAspectRatioID]);
export type VideoAspectRatio = z.infer<typeof zVideoAspectRatio>;
export const isVideoAspectRatio = (v: unknown): v is VideoAspectRatio => zVideoAspectRatio.safeParse(v).success;
export const zVeo3Resolution = z.enum(['720p', '1080p']);
type Veo3Resolution = z.infer<typeof zVeo3Resolution>;
export const isVeo3Resolution = (v: unknown): v is Veo3Resolution => zVeo3Resolution.safeParse(v).success;
export const RESOLUTION_MAP: Record<Veo3Resolution | RunwayResolution, Dimensions> = {
'720p': { width: 1280, height: 720 },
'1080p': { width: 1920, height: 1080 },
};
export const zRunwayResolution = z.enum(['720p']);
type RunwayResolution = z.infer<typeof zRunwayResolution>;
export const isRunwayResolution = (v: unknown): v is RunwayResolution => zRunwayResolution.safeParse(v).success;
export const zVideoResolution = z.union([zVeo3Resolution, zRunwayResolution]);
export type VideoResolution = z.infer<typeof zVideoResolution>;
const zAspectRatioConfig = z.object({
id: zAspectRatioID,
value: z.number().gt(0),
@@ -495,6 +523,24 @@ export const DEFAULT_ASPECT_RATIO_CONFIG: AspectRatioConfig = {
isLocked: false,
};
const zVeo3DurationID = z.enum(['8']);
type Veo3Duration = z.infer<typeof zVeo3DurationID>;
export const isVeo3DurationID = (v: unknown): v is Veo3Duration => zVeo3DurationID.safeParse(v).success;
export const VEO3_DURATIONS: Record<Veo3Duration, string> = {
'8': '8 seconds',
};
const zRunwayDurationID = z.enum(['5', '10']);
type RunwayDuration = z.infer<typeof zRunwayDurationID>;
export const isRunwayDurationID = (v: unknown): v is RunwayDuration => zRunwayDurationID.safeParse(v).success;
export const RUNWAY_DURATIONS: Record<RunwayDuration, string> = {
'5': '5 seconds',
'10': '10 seconds',
};
export const zVideoDuration = z.union([zVeo3DurationID, zRunwayDurationID]);
export type VideoDuration = z.infer<typeof zVideoDuration>;
const zBboxState = z.object({
rect: z.object({
x: z.number().int(),
@@ -512,18 +558,13 @@ const zBboxState = z.object({
});
const zDimensionsState = z.object({
// TODO(psyche): There is no concept of x/y coords for the dimensions state here... It's just width and height.
// Remove the extraneous data.
rect: z.object({
x: z.number().int(),
y: z.number().int(),
width: zParameterImageDimension,
height: zParameterImageDimension,
}),
width: zParameterImageDimension,
height: zParameterImageDimension,
aspectRatio: zAspectRatioConfig,
});
export const zParamsState = z.object({
_version: z.literal(1),
maskBlur: z.number(),
maskBlurMethod: zParameterMaskBlurMethod,
canvasCoherenceMode: zParameterCanvasCoherenceMode,
@@ -571,6 +612,7 @@ export const zParamsState = z.object({
});
export type ParamsState = z.infer<typeof zParamsState>;
export const getInitialParamsState = (): ParamsState => ({
_version: 1,
maskBlur: 16,
maskBlurMethod: 'box',
canvasCoherenceMode: 'Gaussian Blur',
@@ -615,7 +657,8 @@ export const getInitialParamsState = (): ParamsState => ({
clipGEmbedModel: null,
controlLora: null,
dimensions: {
rect: { x: 0, y: 0, width: 512, height: 512 },
width: 512,
height: 512,
aspectRatio: deepClone(DEFAULT_ASPECT_RATIO_CONFIG),
},
});

View File

@@ -0,0 +1,28 @@
import { useDeleteVideoModalApi } from 'features/deleteVideoModal/store/state';
import { useCallback, useMemo } from 'react';
import type { VideoDTO } from 'services/api/types';
export const useDeleteVideo = (videoDTO?: VideoDTO | null) => {
const deleteImageModal = useDeleteVideoModalApi();
const isEnabled = useMemo(() => {
if (!videoDTO) {
return;
}
return true;
}, [videoDTO]);
const _delete = useCallback(() => {
if (!videoDTO) {
return;
}
if (!isEnabled) {
return;
}
deleteImageModal.delete([videoDTO.video_id]);
}, [deleteImageModal, videoDTO, isEnabled]);
return {
delete: _delete,
isEnabled,
};
};

View File

@@ -12,7 +12,7 @@ import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
import type { CanvasState, RefImagesState } from 'features/controlLayers/store/types';
import type { ImageUsage } from 'features/deleteImageModal/store/types';
import { selectGetImageNamesQueryArgs } from 'features/gallery/store/gallerySelectors';
import { imageSelected } from 'features/gallery/store/gallerySlice';
import { itemSelected } from 'features/gallery/store/gallerySlice';
import { fieldImageCollectionValueChanged, fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import { selectNodesSlice } from 'features/nodes/store/selectors';
import type { NodesState } from 'features/nodes/store/types';
@@ -89,9 +89,15 @@ const handleDeletions = async (image_names: string[], store: AppStore) => {
const newImageNames = data?.image_names.filter((name) => !deleted_images.includes(name)) || [];
const newSelectedImage = newImageNames[index ?? 0] || null;
if (intersection(state.gallery.selection, image_names).length > 0) {
// Some selected images were deleted, clear selection
dispatch(imageSelected(newSelectedImage));
const galleryImageNames = state.gallery.selection.map((s) => s.id);
if (intersection(galleryImageNames, image_names).length > 0) {
if (newSelectedImage) {
// Some selected images were deleted, clear selection
dispatch(itemSelected({ type: 'image', id: newSelectedImage }));
} else {
dispatch(itemSelected(null));
}
}
// We need to reset the features where the image is in use - none of these work if their image(s) don't exist

View File

@@ -0,0 +1,36 @@
import type { IconButtonProps } from '@invoke-ai/ui-library';
import { IconButton } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppSelector } from 'app/store/storeHooks';
import { selectSelectionCount } from 'features/gallery/store/gallerySelectors';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiTrashSimpleBold } from 'react-icons/pi';
import { $isConnected } from 'services/events/stores';
type Props = Omit<IconButtonProps, 'aria-label'> & {
onClick: () => void;
};
export const DeleteVideoButton = memo((props: Props) => {
const { onClick, isDisabled } = props;
const { t } = useTranslation();
const isConnected = useStore($isConnected);
const count = useAppSelector(selectSelectionCount);
const labelMessage: string = `${t('gallery.deleteVideo', { count })} (Del)`;
return (
<IconButton
onClick={onClick}
icon={<PiTrashSimpleBold />}
tooltip={labelMessage}
aria-label={labelMessage}
isDisabled={isDisabled || !isConnected}
colorScheme="error"
variant="link"
alignSelf="stretch"
/>
);
});
DeleteVideoButton.displayName = 'DeleteVideoButton';

View File

@@ -0,0 +1,43 @@
import { ConfirmationAlertDialog, Flex, FormControl, FormLabel, Switch, Text } from '@invoke-ai/ui-library';
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
import { useDeleteVideoModalApi, useDeleteVideoModalState } from 'features/deleteVideoModal/store/state';
import { selectSystemShouldConfirmOnDelete, setShouldConfirmOnDelete } from 'features/system/store/systemSlice';
import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const DeleteVideoModal = memo(() => {
const state = useDeleteVideoModalState();
const api = useDeleteVideoModalApi();
const { dispatch } = useAppStore();
const { t } = useTranslation();
const shouldConfirmOnDelete = useAppSelector(selectSystemShouldConfirmOnDelete);
const handleChangeShouldConfirmOnDelete = useCallback(
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldConfirmOnDelete(!e.target.checked)),
[dispatch]
);
return (
<ConfirmationAlertDialog
title={`${t('gallery.deleteVideo', { count: state.video_ids.length })}`}
isOpen={state.isOpen}
onClose={api.close}
cancelButtonText={t('common.cancel')}
acceptButtonText={t('common.delete')}
acceptCallback={api.confirm}
cancelCallback={api.cancel}
useInert={false}
>
<Flex direction="column" gap={3}>
<Text>{t('gallery.deleteVideoPermanent')}</Text>
<Text>{t('common.areYouSure')}</Text>
<FormControl>
<FormLabel>{t('common.dontAskMeAgain')}</FormLabel>
<Switch isChecked={!shouldConfirmOnDelete} onChange={handleChangeShouldConfirmOnDelete} />
</FormControl>
</Flex>
</ConfirmationAlertDialog>
);
});
DeleteVideoModal.displayName = 'DeleteVideoModal';

View File

@@ -0,0 +1,111 @@
import { useStore } from '@nanostores/react';
import type { AppStore } from 'app/store/store';
import { useAppStore } from 'app/store/storeHooks';
import { intersection } from 'es-toolkit/compat';
import { selectGetVideoIdsQueryArgs } from 'features/gallery/store/gallerySelectors';
import { itemSelected } from 'features/gallery/store/gallerySlice';
import { selectSystemShouldConfirmOnDelete } from 'features/system/store/systemSlice';
import { atom } from 'nanostores';
import { useMemo } from 'react';
import { videosApi } from 'services/api/endpoints/videos';
// Implements an awaitable modal dialog for deleting images
type DeleteVideosModalState = {
video_ids: string[];
isOpen: boolean;
resolve?: () => void;
reject?: (reason?: string) => void;
};
const getInitialState = (): DeleteVideosModalState => ({
video_ids: [],
isOpen: false,
});
const $deleteVideosModalState = atom<DeleteVideosModalState>(getInitialState());
const deleteVideosWithDialog = async (video_ids: string[], store: AppStore): Promise<void> => {
const { getState } = store;
const shouldConfirmOnDelete = selectSystemShouldConfirmOnDelete(getState());
if (!shouldConfirmOnDelete) {
// If we don't need to confirm and the resources are not in use, delete them directly
await handleDeletions(video_ids, store);
return;
}
return new Promise<void>((resolve, reject) => {
$deleteVideosModalState.set({
video_ids,
isOpen: true,
resolve,
reject,
});
});
};
const handleDeletions = async (video_ids: string[], store: AppStore) => {
try {
const { dispatch, getState } = store;
const state = getState();
const { data } = videosApi.endpoints.getVideoIds.select(selectGetVideoIdsQueryArgs(state))(state);
const index = data?.video_ids.findIndex((id) => id === video_ids[0]);
const { deleted_videos } = await dispatch(
videosApi.endpoints.deleteVideos.initiate({ video_ids }, { track: false })
).unwrap();
const newVideoIds = data?.video_ids.filter((id) => !deleted_videos.includes(id)) || [];
const newSelectedVideoId = newVideoIds[index ?? 0] || null;
if (
intersection(
state.gallery.selection.map((s) => s.id),
video_ids
).length > 0 &&
newSelectedVideoId
) {
// Some selected images were deleted, clear selection
dispatch(itemSelected({ type: 'video', id: newSelectedVideoId }));
}
} catch {
// no-op
}
};
const confirmDeletion = async (store: AppStore) => {
const state = $deleteVideosModalState.get();
await handleDeletions(state.video_ids, store);
state.resolve?.();
closeSilently();
};
const cancelDeletion = () => {
const state = $deleteVideosModalState.get();
state.reject?.('User canceled');
closeSilently();
};
const closeSilently = () => {
$deleteVideosModalState.set(getInitialState());
};
export const useDeleteVideoModalState = () => {
const state = useStore($deleteVideosModalState);
return state;
};
export const useDeleteVideoModalApi = () => {
const store = useAppStore();
const api = useMemo(
() => ({
delete: (video_ids: string[]) => deleteVideosWithDialog(video_ids, store),
confirm: () => confirmDeletion(store),
cancel: cancelDeletion,
close: closeSilently,
}),
[store]
);
return api;
};

View File

@@ -21,7 +21,7 @@ const DndDragPreviewMultipleImage = memo(({ image_names }: { image_names: string
borderRadius="base"
>
<Heading>{image_names.length}</Heading>
<Heading size="sm">{t('parameters.images')}</Heading>
<Heading size="sm">{t('parameters.images_withCount', { count: image_names.length })}</Heading>
</Flex>
);
});

View File

@@ -0,0 +1,63 @@
import type { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
import { Flex, Heading } from '@invoke-ai/ui-library';
import type { MultipleVideoDndSourceData } from 'features/dnd/dnd';
import { DND_IMAGE_DRAG_PREVIEW_SIZE, preserveOffsetOnSourceFallbackCentered } from 'features/dnd/util';
import { memo } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
import type { Param0 } from 'tsafe';
const DndDragPreviewMultipleVideo = memo(({ video_ids }: { video_ids: string[] }) => {
const { t } = useTranslation();
return (
<Flex
w={DND_IMAGE_DRAG_PREVIEW_SIZE}
h={DND_IMAGE_DRAG_PREVIEW_SIZE}
alignItems="center"
justifyContent="center"
flexDir="column"
bg="base.900"
borderRadius="base"
>
<Heading>{video_ids.length}</Heading>
<Heading size="sm">{t('parameters.videos_withCount', { count: video_ids.length })}</Heading>
</Flex>
);
});
DndDragPreviewMultipleVideo.displayName = 'DndDragPreviewMultipleVideo';
export type DndDragPreviewMultipleVideoState = {
type: 'multiple-video';
container: HTMLElement;
video_ids: string[];
};
export const createMultipleVideoDragPreview = (arg: DndDragPreviewMultipleVideoState) =>
createPortal(<DndDragPreviewMultipleVideo video_ids={arg.video_ids} />, arg.container);
type SetMultipleDragPreviewArg = {
multipleVideoDndData: MultipleVideoDndSourceData;
setDragPreviewState: (dragPreviewState: DndDragPreviewMultipleVideoState | null) => void;
onGenerateDragPreviewArgs: Param0<Param0<typeof draggable>['onGenerateDragPreview']>;
};
export const setMultipleVideoDragPreview = ({
multipleVideoDndData,
onGenerateDragPreviewArgs,
setDragPreviewState,
}: SetMultipleDragPreviewArg) => {
const { nativeSetDragImage, source, location } = onGenerateDragPreviewArgs;
setCustomNativeDragPreview({
render({ container }) {
setDragPreviewState({ type: 'multiple-video', container, video_ids: multipleVideoDndData.payload.video_ids });
return () => setDragPreviewState(null);
},
nativeSetDragImage,
getOffset: preserveOffsetOnSourceFallbackCentered({
element: source.element,
input: location.current.input,
}),
});
};

View File

@@ -0,0 +1,69 @@
import type { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
import { chakra, Flex } from '@invoke-ai/ui-library';
import type { SingleVideoDndSourceData } from 'features/dnd/dnd';
import { DND_IMAGE_DRAG_PREVIEW_SIZE, preserveOffsetOnSourceFallbackCentered } from 'features/dnd/util';
import { GalleryVideoPlaceholder } from 'features/gallery/components/ImageGrid/GalleryVideoPlaceholder';
import { memo } from 'react';
import { createPortal } from 'react-dom';
import type { VideoDTO } from 'services/api/types';
import type { Param0 } from 'tsafe';
const ChakraImg = chakra('img');
const DndDragPreviewSingleVideo = memo(({ videoDTO }: { videoDTO: VideoDTO }) => {
return (
<Flex position="relative" w={DND_IMAGE_DRAG_PREVIEW_SIZE} h={DND_IMAGE_DRAG_PREVIEW_SIZE}>
<GalleryVideoPlaceholder />
<ChakraImg
position="absolute"
margin="auto"
maxW="full"
maxH="full"
objectFit="contain"
borderRadius="base"
borderWidth={2}
borderColor="invokeBlue.300"
borderStyle="solid"
cursor="grabbing"
src={videoDTO.thumbnail_url}
/>
</Flex>
);
});
DndDragPreviewSingleVideo.displayName = 'DndDragPreviewSingleVideo';
export type DndDragPreviewSingleVideoState = {
type: 'single-video';
container: HTMLElement;
videoDTO: VideoDTO;
};
export const createSingleVideoDragPreview = (arg: DndDragPreviewSingleVideoState) =>
createPortal(<DndDragPreviewSingleVideo videoDTO={arg.videoDTO} />, arg.container);
type SetSingleDragPreviewArg = {
singleVideoDndData: SingleVideoDndSourceData;
setDragPreviewState: (dragPreviewState: DndDragPreviewSingleVideoState | null) => void;
onGenerateDragPreviewArgs: Param0<Param0<typeof draggable>['onGenerateDragPreview']>;
};
export const setSingleVideoDragPreview = ({
singleVideoDndData,
onGenerateDragPreviewArgs,
setDragPreviewState,
}: SetSingleDragPreviewArg) => {
const { nativeSetDragImage, source, location } = onGenerateDragPreviewArgs;
setCustomNativeDragPreview({
render({ container }) {
setDragPreviewState({ type: 'single-video', container, videoDTO: singleVideoDndData.payload.videoDTO });
return () => setDragPreviewState(null);
},
nativeSetDragImage,
getOffset: preserveOffsetOnSourceFallbackCentered({
element: source.element,
input: location.current.input,
}),
});
};

View File

@@ -2,12 +2,14 @@ import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import type { ImageProps, SystemStyleObject } from '@invoke-ai/ui-library';
import { Image } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { $crossOrigin } from 'app/store/nanostores/authToken';
import { useAppStore } from 'app/store/storeHooks';
import { singleImageDndSource } from 'features/dnd/dnd';
import type { DndDragPreviewSingleImageState } from 'features/dnd/DndDragPreviewSingleImage';
import { createSingleImageDragPreview, setSingleImageDragPreview } from 'features/dnd/DndDragPreviewSingleImage';
import { firefoxDndFix } from 'features/dnd/util';
import { useImageContextMenu } from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
import { useImageContextMenu } from 'features/gallery/components/ContextMenu/ImageContextMenu';
import { forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from 'react';
import type { ImageDTO } from 'services/api/types';
@@ -29,6 +31,8 @@ type Props = {
export const DndImage = memo(
forwardRef(({ imageDTO, asThumbnail, ...rest }: Props, forwardedRef) => {
const store = useAppStore();
const crossOrigin = useStore($crossOrigin);
const [isDragging, setIsDragging] = useState(false);
const ref = useRef<HTMLImageElement>(null);
useImperativeHandle(forwardedRef, () => ref.current!, []);
@@ -76,6 +80,7 @@ export const DndImage = memo(
height={imageDTO.height}
sx={sx}
data-is-dragging={isDragging}
crossOrigin={!asThumbnail ? crossOrigin : undefined}
{...rest}
/>
{dragPreviewState?.type === 'single-image' ? createSingleImageDragPreview(dragPreviewState) : null}

View File

@@ -9,9 +9,11 @@ import { selectComparisonImages } from 'features/gallery/components/ImageViewer/
import type { BoardId } from 'features/gallery/store/types';
import {
addImagesToBoard,
addVideosToBoard,
createNewCanvasEntityFromImage,
newCanvasFromImage,
removeImagesFromBoard,
removeVideosFromBoard,
replaceCanvasEntityObjectsWithImage,
setComparisonImage,
setGlobalReferenceImage,
@@ -22,9 +24,10 @@ import {
import { fieldImageCollectionValueChanged } from 'features/nodes/store/nodesSlice';
import { selectFieldInputInstanceSafe, selectNodesSlice } from 'features/nodes/store/selectors';
import { type FieldIdentifier, isImageFieldCollectionInputInstance } from 'features/nodes/types/field';
import { startingFrameImageChanged } from 'features/parameters/store/videoSlice';
import { expandPrompt } from 'features/prompt/PromptExpansion/expand';
import { promptExpansionApi } from 'features/prompt/PromptExpansion/state';
import type { ImageDTO } from 'services/api/types';
import type { ImageDTO, VideoDTO } from 'services/api/types';
import type { JsonObject } from 'type-fest';
const log = logger('dnd');
@@ -70,6 +73,35 @@ type DndSource<SourceData extends DndData> = {
typeGuard: ReturnType<typeof buildTypeGuard<SourceData>>;
getData: ReturnType<typeof buildGetData<SourceData>>;
};
//#region Single Video
const _singleVideo = buildTypeAndKey('single-video');
export type SingleVideoDndSourceData = DndData<
typeof _singleVideo.type,
typeof _singleVideo.key,
{ videoDTO: VideoDTO }
>;
export const singleVideoDndSource: DndSource<SingleVideoDndSourceData> = {
..._singleVideo,
typeGuard: buildTypeGuard(_singleVideo.key),
getData: buildGetData(_singleVideo.key, _singleVideo.type),
};
//#endregion
//#region Multiple Image
const _multipleVideo = buildTypeAndKey('multiple-video');
export type MultipleVideoDndSourceData = DndData<
typeof _multipleVideo.type,
typeof _multipleVideo.key,
{ video_ids: string[]; board_id: BoardId }
>;
export const multipleVideoDndSource: DndSource<MultipleVideoDndSourceData> = {
..._multipleVideo,
typeGuard: buildTypeGuard(_multipleVideo.key),
getData: buildGetData(_multipleVideo.key, _multipleVideo.type),
};
//#endregion
//#region Single Image
const _singleImage = buildTypeAndKey('single-image');
export type SingleImageDndSourceData = DndData<
@@ -443,12 +475,22 @@ export type AddImageToBoardDndTargetData = DndData<
>;
export const addImageToBoardDndTarget: DndTarget<
AddImageToBoardDndTargetData,
SingleImageDndSourceData | MultipleImageDndSourceData
SingleImageDndSourceData | MultipleImageDndSourceData | SingleVideoDndSourceData | MultipleVideoDndSourceData
> = {
..._addToBoard,
typeGuard: buildTypeGuard(_addToBoard.key),
getData: buildGetData(_addToBoard.key, _addToBoard.type),
isValid: ({ sourceData, targetData }) => {
if (singleVideoDndSource.typeGuard(sourceData)) {
const currentBoard = sourceData.payload.videoDTO.board_id ?? 'none';
const destinationBoard = targetData.payload.boardId;
return currentBoard !== destinationBoard;
}
if (multipleVideoDndSource.typeGuard(sourceData)) {
const currentBoard = sourceData.payload.board_id;
const destinationBoard = targetData.payload.boardId;
return currentBoard !== destinationBoard;
}
if (singleImageDndSource.typeGuard(sourceData)) {
const currentBoard = sourceData.payload.imageDTO.board_id ?? 'none';
const destinationBoard = targetData.payload.boardId;
@@ -462,6 +504,18 @@ export const addImageToBoardDndTarget: DndTarget<
return false;
},
handler: ({ sourceData, targetData, dispatch }) => {
if (singleVideoDndSource.typeGuard(sourceData)) {
const { videoDTO } = sourceData.payload;
const { boardId } = targetData.payload;
addVideosToBoard({ video_ids: [videoDTO.video_id], boardId, dispatch });
}
if (multipleVideoDndSource.typeGuard(sourceData)) {
const { video_ids } = sourceData.payload;
const { boardId } = targetData.payload;
addVideosToBoard({ video_ids, boardId, dispatch });
}
if (singleImageDndSource.typeGuard(sourceData)) {
const { imageDTO } = sourceData.payload;
const { boardId } = targetData.payload;
@@ -487,7 +541,7 @@ export type RemoveImageFromBoardDndTargetData = DndData<
>;
export const removeImageFromBoardDndTarget: DndTarget<
RemoveImageFromBoardDndTargetData,
SingleImageDndSourceData | MultipleImageDndSourceData
SingleImageDndSourceData | MultipleImageDndSourceData | SingleVideoDndSourceData | MultipleVideoDndSourceData
> = {
..._removeFromBoard,
typeGuard: buildTypeGuard(_removeFromBoard.key),
@@ -503,6 +557,16 @@ export const removeImageFromBoardDndTarget: DndTarget<
return currentBoard !== 'none';
}
if (singleVideoDndSource.typeGuard(sourceData)) {
const currentBoard = sourceData.payload.videoDTO.board_id ?? 'none';
return currentBoard !== 'none';
}
if (multipleVideoDndSource.typeGuard(sourceData)) {
const currentBoard = sourceData.payload.board_id;
return currentBoard !== 'none';
}
return false;
},
handler: ({ sourceData, dispatch }) => {
@@ -515,6 +579,16 @@ export const removeImageFromBoardDndTarget: DndTarget<
const { image_names } = sourceData.payload;
removeImagesFromBoard({ image_names, dispatch });
}
if (singleVideoDndSource.typeGuard(sourceData)) {
const { videoDTO } = sourceData.payload;
removeVideosFromBoard({ video_ids: [videoDTO.video_id], dispatch });
}
if (multipleVideoDndSource.typeGuard(sourceData)) {
const { video_ids } = sourceData.payload;
removeVideosFromBoard({ video_ids, dispatch });
}
},
};
@@ -548,6 +622,30 @@ export const promptGenerationFromImageDndTarget: DndTarget<
};
//#endregion
//#region Video Frame From Image
const _videoFrameFromImage = buildTypeAndKey('video-frame-from-image');
type VideoFrameFromImageDndTargetData = DndData<
typeof _videoFrameFromImage.type,
typeof _videoFrameFromImage.key,
{ frame: 'start' | 'end' }
>;
export const videoFrameFromImageDndTarget: DndTarget<VideoFrameFromImageDndTargetData, SingleImageDndSourceData> = {
..._videoFrameFromImage,
typeGuard: buildTypeGuard(_videoFrameFromImage.key),
getData: buildGetData(_videoFrameFromImage.key, _videoFrameFromImage.type),
isValid: ({ sourceData }) => {
if (singleImageDndSource.typeGuard(sourceData)) {
return true;
}
return false;
},
handler: ({ sourceData, dispatch }) => {
const { imageDTO } = sourceData.payload;
dispatch(startingFrameImageChanged(imageDTOToImageWithDims(imageDTO)));
},
};
//#endregion
export const dndTargets = [
setGlobalReferenceImageDndTarget,
addGlobalReferenceImageDndTarget,
@@ -562,6 +660,7 @@ export const dndTargets = [
addImageToBoardDndTarget,
removeImageFromBoardDndTarget,
promptGenerationFromImageDndTarget,
videoFrameFromImageDndTarget,
] as const;
export type AnyDndTarget = (typeof dndTargets)[number];

View File

@@ -4,7 +4,13 @@ import { logger } from 'app/logging/logger';
import { getStore } from 'app/store/nanostores/store';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { parseify } from 'common/util/serialize';
import { dndTargets, multipleImageDndSource, singleImageDndSource } from 'features/dnd/dnd';
import {
dndTargets,
multipleImageDndSource,
multipleVideoDndSource,
singleImageDndSource,
singleVideoDndSource,
} from 'features/dnd/dnd';
import { useEffect } from 'react';
const log = logger('dnd');
@@ -19,7 +25,12 @@ export const useDndMonitor = () => {
const sourceData = source.data;
// Check for allowed sources
if (!singleImageDndSource.typeGuard(sourceData) && !multipleImageDndSource.typeGuard(sourceData)) {
if (
!singleImageDndSource.typeGuard(sourceData) &&
!multipleImageDndSource.typeGuard(sourceData) &&
!singleVideoDndSource.typeGuard(sourceData) &&
!multipleVideoDndSource.typeGuard(sourceData)
) {
return false;
}

View File

@@ -1,26 +1,23 @@
import { Flex, Image, Text } from '@invoke-ai/ui-library';
import { skipToken } from '@reduxjs/toolkit/query';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { useTranslation } from 'react-i18next';
import { useGetBoardAssetsTotalQuery, useGetBoardImagesTotalQuery } from 'services/api/endpoints/boards';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import type { BoardDTO } from 'services/api/types';
type Props = {
board: BoardDTO | null;
boardCounts: {
image_count: number;
asset_count: number;
video_count: number;
};
};
export const BoardTooltip = ({ board }: Props) => {
export const BoardTooltip = ({ board, boardCounts }: Props) => {
const { t } = useTranslation();
const { imagesTotal } = useGetBoardImagesTotalQuery(board?.board_id || 'none', {
selectFromResult: ({ data }) => {
return { imagesTotal: data?.total ?? 0 };
},
});
const { assetsTotal } = useGetBoardAssetsTotalQuery(board?.board_id || 'none', {
selectFromResult: ({ data }) => {
return { assetsTotal: data?.total ?? 0 };
},
});
const isVideoEnabled = useFeatureStatus('video');
const { currentData: coverImage } = useGetImageDTOQuery(board?.cover_image_name ?? skipToken);
return (
@@ -39,8 +36,10 @@ export const BoardTooltip = ({ board }: Props) => {
<Flex flexDir="column" alignItems="center">
{board && <Text fontWeight="semibold">{board.board_name}</Text>}
<Text noOfLines={1}>
{t('boards.imagesWithCount', { count: imagesTotal })}, {t('boards.assetsWithCount', { count: assetsTotal })}
{t('boards.imagesWithCount', { count: boardCounts.image_count })},{' '}
{t('boards.assetsWithCount', { count: boardCounts.asset_count })}
</Text>
{isVideoEnabled && <Text noOfLines={1}>{t('boards.videosWithCount', { count: boardCounts.video_count })}</Text>}
{board?.archived && <Text>({t('boards.archived')})</Text>}
</Flex>
</Flex>

View File

@@ -15,6 +15,7 @@ import {
selectSelectedBoardId,
} from 'features/gallery/store/gallerySelectors';
import { autoAddBoardIdChanged, boardIdSelected } from 'features/gallery/store/gallerySlice';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArchiveBold, PiImageSquare } from 'react-icons/pi';
@@ -36,6 +37,7 @@ const GalleryBoard = ({ board, isSelected }: GalleryBoardProps) => {
const autoAddBoardId = useAppSelector(selectAutoAddBoardId);
const autoAssignBoardOnClick = useAppSelector(selectAutoAssignBoardOnClick);
const selectedBoardId = useAppSelector(selectSelectedBoardId);
const isVideoEnabled = useFeatureStatus('video');
const onClick = useCallback(() => {
if (selectedBoardId !== board.board_id) {
dispatch(boardIdSelected({ boardId: board.board_id }));
@@ -50,11 +52,26 @@ const GalleryBoard = ({ board, isSelected }: GalleryBoardProps) => {
[board.board_id]
);
const boardCounts = useMemo(
() => ({
image_count: board.image_count,
asset_count: board.asset_count,
video_count: board.video_count,
}),
[board]
);
return (
<Box position="relative" w="full" h={12}>
<BoardContextMenu board={board}>
{(ref) => (
<Tooltip label={<BoardTooltip board={board} />} openDelay={1000} placement="left" closeOnScroll p={2}>
<Tooltip
label={<BoardTooltip board={board} boardCounts={boardCounts} />}
openDelay={1000}
placement="right"
closeOnScroll
p={2}
>
<Flex
ref={ref}
onClick={onClick}
@@ -71,12 +88,17 @@ const GalleryBoard = ({ board, isSelected }: GalleryBoardProps) => {
h="full"
>
<CoverImage board={board} />
<Flex w="full">
<Flex flex={1}>
<BoardEditableTitle board={board} isSelected={isSelected} />
</Flex>
{autoAddBoardId === board.board_id && <AutoAddBadge />}
{board.archived && <Icon as={PiArchiveBold} fill="base.300" />}
<Text variant="subtext">{board.image_count}</Text>
<Flex justifyContent="flex-end">
<Text variant="subtext">
{board.image_count} | {isVideoEnabled && `${board.video_count} | `}
{board.asset_count}
</Text>
</Flex>
</Flex>
</Tooltip>
)}

View File

@@ -13,9 +13,14 @@ import {
selectBoardSearchText,
} from 'features/gallery/store/gallerySelectors';
import { autoAddBoardIdChanged, boardIdSelected } from 'features/gallery/store/gallerySlice';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetBoardImagesTotalQuery } from 'services/api/endpoints/boards';
import {
useGetBoardAssetsTotalQuery,
useGetBoardImagesTotalQuery,
useGetBoardVideosTotalQuery,
} from 'services/api/endpoints/boards';
import { useBoardName } from 'services/api/hooks/useBoardName';
interface Props {
@@ -28,11 +33,23 @@ const _hover: SystemStyleObject = {
const NoBoardBoard = memo(({ isSelected }: Props) => {
const dispatch = useAppDispatch();
const isVideoEnabled = useFeatureStatus('video');
const { imagesTotal } = useGetBoardImagesTotalQuery('none', {
selectFromResult: ({ data }) => {
return { imagesTotal: data?.total ?? 0 };
},
});
const { assetsTotal } = useGetBoardAssetsTotalQuery('none', {
selectFromResult: ({ data }) => {
return { assetsTotal: data?.total ?? 0 };
},
});
const { videoTotal } = useGetBoardVideosTotalQuery('none', {
skip: !isVideoEnabled,
selectFromResult: ({ data }) => {
return { videoTotal: data?.total ?? 0 };
},
});
const autoAddBoardId = useAppSelector(selectAutoAddBoardId);
const autoAssignBoardOnClick = useAppSelector(selectAutoAssignBoardOnClick);
const boardSearchText = useAppSelector(selectBoardSearchText);
@@ -56,7 +73,17 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
<Box position="relative" w="full" h={12}>
<NoBoardBoardContextMenu>
{(ref) => (
<Tooltip label={<BoardTooltip board={null} />} openDelay={1000} placement="left" closeOnScroll>
<Tooltip
label={
<BoardTooltip
board={null}
boardCounts={{ image_count: imagesTotal, asset_count: assetsTotal, video_count: videoTotal }}
/>
}
openDelay={1000}
placement="right"
closeOnScroll
>
<Flex
ref={ref}
onClick={handleSelectBoard}
@@ -92,7 +119,10 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
{boardName}
</Text>
{autoAddBoardId === 'none' && <AutoAddBadge />}
<Text variant="subtext">{imagesTotal}</Text>
<Text variant="subtext">
{imagesTotal} | {isVideoEnabled && `${videoTotal} | `}
{assetsTotal}
</Text>
</Flex>
</Tooltip>
)}

View File

@@ -3,8 +3,8 @@ import { Menu, MenuButton, MenuList, Portal, useGlobalMenuClose } from '@invoke-
import { useStore } from '@nanostores/react';
import { useAppSelector } from 'app/store/storeHooks';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import MultipleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems';
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
import MultipleSelectionMenuItems from 'features/gallery/components/ContextMenu/MultipleSelectionMenuItems';
import SingleSelectionMenuItems from 'features/gallery/components/ContextMenu/SingleSelectionMenuItems';
import { selectSelectionCount } from 'features/gallery/store/gallerySelectors';
import { map } from 'nanostores';
import type { RefObject } from 'react';

View File

@@ -0,0 +1,35 @@
import { MenuItem } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import {
imagesToChangeSelected,
isModalOpenChanged,
videosToChangeSelected,
} from 'features/changeBoardModal/store/slice';
import { useItemDTOContext } from 'features/gallery/contexts/ItemDTOContext';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiFoldersBold } from 'react-icons/pi';
import { isImageDTO } from 'services/api/types';
export const ContextMenuItemChangeBoard = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const itemDTO = useItemDTOContext();
const onClick = useCallback(() => {
if (isImageDTO(itemDTO)) {
dispatch(imagesToChangeSelected([itemDTO.image_name]));
} else {
dispatch(videosToChangeSelected([itemDTO.video_id]));
}
dispatch(isModalOpenChanged(true));
}, [dispatch, itemDTO]);
return (
<MenuItem icon={<PiFoldersBold />} onClickCapture={onClick}>
{t('boards.changeBoard')}
</MenuItem>
);
});
ContextMenuItemChangeBoard.displayName = 'ContextMenuItemChangeBoard';

View File

@@ -1,18 +1,23 @@
import { IconMenuItem } from 'common/components/IconMenuItem';
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { useItemDTOContext } from 'features/gallery/contexts/ItemDTOContext';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiCopyBold } from 'react-icons/pi';
import { isImageDTO } from 'services/api/types';
export const ImageMenuItemCopy = memo(() => {
export const ContextMenuItemCopy = memo(() => {
const { t } = useTranslation();
const imageDTO = useImageDTOContext();
const itemDTO = useItemDTOContext();
const copyImageToClipboard = useCopyImageToClipboard();
const onClick = useCallback(() => {
copyImageToClipboard(imageDTO.image_url);
}, [copyImageToClipboard, imageDTO.image_url]);
if (isImageDTO(itemDTO)) {
copyImageToClipboard(itemDTO.image_url);
} else {
// copyVideoToClipboard(itemDTO.video_url);
}
}, [copyImageToClipboard, itemDTO]);
return (
<IconMenuItem
@@ -24,4 +29,4 @@ export const ImageMenuItemCopy = memo(() => {
);
});
ImageMenuItemCopy.displayName = 'ImageMenuItemCopy';
ContextMenuItemCopy.displayName = 'ContextMenuItemCopy';

View File

@@ -1,22 +1,25 @@
import { IconMenuItem } from 'common/components/IconMenuItem';
import { useDeleteImageModalApi } from 'features/deleteImageModal/store/state';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { useItemDTOContext } from 'features/gallery/contexts/ItemDTOContext';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiTrashSimpleBold } from 'react-icons/pi';
import { isImageDTO } from 'services/api/types';
export const ImageMenuItemDelete = memo(() => {
export const ContextMenuItemDeleteImage = memo(() => {
const { t } = useTranslation();
const deleteImageModal = useDeleteImageModalApi();
const imageDTO = useImageDTOContext();
const itemDTO = useItemDTOContext();
const onClick = useCallback(async () => {
try {
await deleteImageModal.delete([imageDTO.image_name]);
if (isImageDTO(itemDTO)) {
await deleteImageModal.delete([itemDTO.image_name]);
}
} catch {
// noop;
}
}, [deleteImageModal, imageDTO]);
}, [deleteImageModal, itemDTO]);
return (
<IconMenuItem
@@ -29,4 +32,4 @@ export const ImageMenuItemDelete = memo(() => {
);
});
ImageMenuItemDelete.displayName = 'ImageMenuItemDelete';
ContextMenuItemDeleteImage.displayName = 'ContextMenuItemDeleteImage';

View File

@@ -0,0 +1,35 @@
import { IconMenuItem } from 'common/components/IconMenuItem';
import { useDeleteVideoModalApi } from 'features/deleteVideoModal/store/state';
import { useItemDTOContext } from 'features/gallery/contexts/ItemDTOContext';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiTrashSimpleBold } from 'react-icons/pi';
import { isVideoDTO } from 'services/api/types';
export const ContextMenuItemDeleteVideo = memo(() => {
const { t } = useTranslation();
const deleteVideoModal = useDeleteVideoModalApi();
const itemDTO = useItemDTOContext();
const onClick = useCallback(async () => {
try {
if (isVideoDTO(itemDTO)) {
await deleteVideoModal.delete([itemDTO.video_id]);
}
} catch {
// noop;
}
}, [deleteVideoModal, itemDTO]);
return (
<IconMenuItem
icon={<PiTrashSimpleBold />}
onClickCapture={onClick}
aria-label={t('gallery.deleteVideo', { count: 1 })}
tooltip={t('gallery.deleteVideo', { count: 1 })}
isDestructive
/>
);
});
ContextMenuItemDeleteVideo.displayName = 'ContextMenuItemDeleteVideo';

View File

@@ -0,0 +1,32 @@
import { IconMenuItem } from 'common/components/IconMenuItem';
import { useDownloadItem } from 'common/hooks/useDownloadImage';
import { useItemDTOContext } from 'features/gallery/contexts/ItemDTOContext';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiDownloadSimpleBold } from 'react-icons/pi';
import { isImageDTO } from 'services/api/types';
export const ContextMenuItemDownload = memo(() => {
const { t } = useTranslation();
const itemDTO = useItemDTOContext();
const { downloadItem } = useDownloadItem();
const onClick = useCallback(() => {
if (isImageDTO(itemDTO)) {
downloadItem(itemDTO.image_url, itemDTO.image_name);
} else {
downloadItem(itemDTO.video_url, itemDTO.video_id);
}
}, [downloadItem, itemDTO]);
return (
<IconMenuItem
icon={<PiDownloadSimpleBold />}
aria-label={t('gallery.download')}
tooltip={t('gallery.download')}
onClick={onClick}
/>
);
});
ContextMenuItemDownload.displayName = 'ContextMenuItemDownload';

View File

@@ -0,0 +1,39 @@
import { MenuItem } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useItemDTOContext } from 'features/gallery/contexts/ItemDTOContext';
import { $hasTemplates } from 'features/nodes/store/nodesSlice';
import { useLoadWorkflowWithDialog } from 'features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiFlowArrowBold } from 'react-icons/pi';
import { isImageDTO } from 'services/api/types';
export const ContextMenuItemLoadWorkflow = memo(() => {
const { t } = useTranslation();
const itemDTO = useItemDTOContext();
const loadWorkflowWithDialog = useLoadWorkflowWithDialog();
const hasTemplates = useStore($hasTemplates);
const onClick = useCallback(() => {
if (isImageDTO(itemDTO)) {
loadWorkflowWithDialog({ type: 'image', data: itemDTO.image_name });
} else {
// loadWorkflowWithDialog({ type: 'video', data: itemDTO.video_id });
}
}, [loadWorkflowWithDialog, itemDTO]);
const isDisabled = useMemo(() => {
if (isImageDTO(itemDTO)) {
return !itemDTO.has_workflow || !hasTemplates;
}
return false;
}, [itemDTO, hasTemplates]);
return (
<MenuItem icon={<PiFlowArrowBold />} onClickCapture={onClick} isDisabled={isDisabled}>
{t('nodes.loadWorkflow')}
</MenuItem>
);
});
ContextMenuItemLoadWorkflow.displayName = 'ContextMenuItemLoadWorkflow';

View File

@@ -0,0 +1,63 @@
import { MenuItem } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useItemDTOContext } from 'features/gallery/contexts/ItemDTOContext';
import { boardIdSelected } from 'features/gallery/store/gallerySlice';
import { IMAGE_CATEGORIES } from 'features/gallery/store/types';
import { navigationApi } from 'features/ui/layouts/navigation-api';
import { useGalleryPanel } from 'features/ui/layouts/use-gallery-panel';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo, useCallback, useMemo } from 'react';
import { flushSync } from 'react-dom';
import { useTranslation } from 'react-i18next';
import { PiCrosshairBold } from 'react-icons/pi';
import { isImageDTO } from 'services/api/types';
export const ContextMenuItemLocateInGalery = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const itemDTO = useItemDTOContext();
const activeTab = useAppSelector(selectActiveTab);
const galleryPanel = useGalleryPanel(activeTab);
const isGalleryImage = useMemo(() => {
return !itemDTO.is_intermediate;
}, [itemDTO]);
const onClick = useCallback(() => {
navigationApi.expandRightPanel();
galleryPanel.expand();
if (isImageDTO(itemDTO)) {
flushSync(() => {
dispatch(
boardIdSelected({
boardId: itemDTO.board_id ?? 'none',
select: {
selection: [{ type: 'image', id: itemDTO.image_name }],
galleryView: IMAGE_CATEGORIES.includes(itemDTO.image_category) ? 'images' : 'assets',
},
})
);
});
} else {
flushSync(() => {
dispatch(
boardIdSelected({
boardId: itemDTO.board_id ?? 'none',
select: {
selection: [{ type: 'video', id: itemDTO.video_id }],
galleryView: 'videos',
},
})
);
});
}
}, [dispatch, galleryPanel, itemDTO]);
return (
<MenuItem icon={<PiCrosshairBold />} onClickCapture={onClick} isDisabled={!isGalleryImage}>
{t('boards.locateInGalery')}
</MenuItem>
);
});
ContextMenuItemLocateInGalery.displayName = 'ContextMenuItemLocateInGalery';

View File

@@ -1,7 +1,7 @@
import { Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
import { SubMenuButtonContent, useSubMenu } from 'common/hooks/useSubMenu';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { useRecallAll } from 'features/gallery/hooks/useRecallAll';
import { useItemDTOContext } from 'features/gallery/contexts/ItemDTOContext';
import { useRecallAll } from 'features/gallery/hooks/useRecallAllImageMetadata';
import { useRecallCLIPSkip } from 'features/gallery/hooks/useRecallCLIPSkip';
import { useRecallDimensions } from 'features/gallery/hooks/useRecallDimensions';
import { useRecallPrompts } from 'features/gallery/hooks/useRecallPrompts';
@@ -17,19 +17,21 @@ import {
PiQuotesBold,
PiRulerBold,
} from 'react-icons/pi';
import type { ImageDTO } from 'services/api/types';
export const ImageMenuItemMetadataRecallActionsCanvasGenerateTabs = memo(() => {
export const ContextMenuItemMetadataRecallActionsCanvasGenerateTabs = memo(() => {
const { t } = useTranslation();
const subMenu = useSubMenu();
const imageDTO = useImageDTOContext();
const itemDTO = useItemDTOContext();
const recallAll = useRecallAll(imageDTO);
const recallRemix = useRecallRemix(imageDTO);
const recallPrompts = useRecallPrompts(imageDTO);
const recallSeed = useRecallSeed(imageDTO);
const recallDimensions = useRecallDimensions(imageDTO);
const recallCLIPSkip = useRecallCLIPSkip(imageDTO);
// TODO: Implement video recall metadata actions
const recallAll = useRecallAll(itemDTO as ImageDTO);
const recallRemix = useRecallRemix(itemDTO as ImageDTO);
const recallPrompts = useRecallPrompts(itemDTO as ImageDTO);
const recallSeed = useRecallSeed(itemDTO as ImageDTO);
const recallDimensions = useRecallDimensions(itemDTO as ImageDTO);
const recallCLIPSkip = useRecallCLIPSkip(itemDTO as ImageDTO);
return (
<MenuItem {...subMenu.parentMenuItemProps} icon={<PiArrowBendUpLeftBold />}>
@@ -66,5 +68,5 @@ export const ImageMenuItemMetadataRecallActionsCanvasGenerateTabs = memo(() => {
);
});
ImageMenuItemMetadataRecallActionsCanvasGenerateTabs.displayName =
'ImageMenuItemMetadataRecallActionsCanvasGenerateTabs';
ContextMenuItemMetadataRecallActionsCanvasGenerateTabs.displayName =
'ContextMenuItemMetadataRecallActionsCanvasGenerateTabs';

View File

@@ -1,20 +1,22 @@
import { Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
import { SubMenuButtonContent, useSubMenu } from 'common/hooks/useSubMenu';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { useItemDTOContext } from 'features/gallery/contexts/ItemDTOContext';
import { useRecallPrompts } from 'features/gallery/hooks/useRecallPrompts';
import { useRecallSeed } from 'features/gallery/hooks/useRecallSeed';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArrowBendUpLeftBold, PiPlantBold, PiQuotesBold } from 'react-icons/pi';
import type { ImageDTO } from 'services/api/types';
export const ImageMenuItemMetadataRecallActionsUpscaleTab = memo(() => {
export const ContextMenuItemMetadataRecallActionsUpscaleTab = memo(() => {
const { t } = useTranslation();
const subMenu = useSubMenu();
const imageDTO = useImageDTOContext();
const itemDTO = useItemDTOContext();
const recallPrompts = useRecallPrompts(imageDTO);
const recallSeed = useRecallSeed(imageDTO);
// TODO: Implement video recall metadata actions
const recallPrompts = useRecallPrompts(itemDTO as ImageDTO);
const recallSeed = useRecallSeed(itemDTO as ImageDTO);
return (
<MenuItem {...subMenu.parentMenuItemProps} icon={<PiArrowBendUpLeftBold />}>
@@ -35,4 +37,4 @@ export const ImageMenuItemMetadataRecallActionsUpscaleTab = memo(() => {
);
});
ImageMenuItemMetadataRecallActionsUpscaleTab.displayName = 'ImageMenuItemMetadataRecallActionsUpscaleTab';
ContextMenuItemMetadataRecallActionsUpscaleTab.displayName = 'ContextMenuItemMetadataRecallActionsUpscaleTab';

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