Compare commits

...

44 Commits

Author SHA1 Message Date
psychedelicious
7e1b9567c1 chore: bump version to v5.1.0 2024-10-08 09:50:17 +11:00
psychedelicious
56ef754292 fix(ui): duplicate translation string for "layer" 2024-10-08 08:11:07 +11:00
Phrixus2023
2de99ec32d translationBot(ui): update translation (Chinese (Simplified Han script))
Currently translated at 65.0% (957 of 1471 strings)

Co-authored-by: Phrixus2023 <920414016@qq.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/zh_Hans/
Translation: InvokeAI/Web UI
2024-10-08 07:56:57 +11:00
Riccardo Giovanetti
889e63d585 translationBot(ui): update translation (Italian)
Currently translated at 98.7% (1453 of 1471 strings)

translationBot(ui): update translation (Italian)

Currently translated at 98.7% (1453 of 1471 strings)

translationBot(ui): update translation (Italian)

Currently translated at 98.7% (1452 of 1471 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
2024-10-08 07:56:57 +11:00
Riku
56de2b3a51 feat(ui): allow for a broader range of guidance values for flux models 2024-10-08 07:51:20 +11:00
Alex Ameen
eb40bdb810 docs: list FLUX as supported
Adds FLUX to the list of supported models.
2024-10-07 10:27:56 -04:00
psychedelicious
0840e5fa65 fix(ui): missing translations for canvas drop area 2024-10-07 07:55:28 -04:00
Riccardo Giovanetti
b79f2a4e4f translationBot(ui): update translation (Italian)
Currently translated at 90.6% (1334 of 1471 strings)

translationBot(ui): update translation (Italian)

Currently translated at 85.9% (1265 of 1471 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
2024-10-07 11:44:02 +11:00
Васянатор
76a533e67e translationBot(ui): update translation (Russian)
Currently translated at 100.0% (1471 of 1471 strings)

Co-authored-by: Васянатор <ilabulanov339@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/ru/
Translation: InvokeAI/Web UI
2024-10-07 11:44:02 +11:00
Thomas Bolteau
188974988c translationBot(ui): update translation (French)
Currently translated at 55.5% (817 of 1471 strings)

Co-authored-by: Thomas Bolteau <thomas.bolteau50@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/fr/
Translation: InvokeAI/Web UI
2024-10-07 11:44:02 +11:00
Riku
b47aae2165 translationBot(ui): update translation (German)
Currently translated at 67.2% (989 of 1471 strings)

Co-authored-by: Riku <riku.block@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/de/
Translation: InvokeAI/Web UI
2024-10-07 11:44:02 +11:00
psychedelicious
7105a22e0f chore(ui): bump @invoke-ai/ui-library
- Reverts the `onClick -> onPointerUp` changes, which fixed Apple Pencil interactions of buttons with tooltips but broke things in other subtle ways.
- Adds a default `openDelay` on tooltips of 500ms. This is another way to fix Apple Pencil interactions, and according to some searching online, is the best practice for tooltips anyways. The default behaviour  should be for there to be a delay, and only in specific circumstances should there be no delay. So we'll see how this is received.
2024-10-07 10:05:20 +11:00
psychedelicious
eee4175e4d Revert "fix(ui): Apple Pencil requires onPointerUp instead of onClick"
This reverts commit 2a90f4f59e.
2024-10-07 10:05:20 +11:00
psychedelicious
e0b63559d0 docs(ui): getColorAtCoordinate 2024-10-05 23:41:33 -04:00
psychedelicious
aa54c1f969 feat(ui): fix color picker wrong color, improved perf
The color picker take some time to sample the color from the canvas state. This could cause a race condition where the cursor position changes between the time sampling starts, resulting in the picker showing the wrong color. Sometimes it picks up the color picker tool preview!

To resolve this, the color picker's color syncing is now throttled to once per animation frame. Besides fixing the incorrect color issue, it improves the perf substantially by reducing number of samples we take.
2024-10-05 23:41:33 -04:00
psychedelicious
87fdea4cc6 feat(ui): updated cursor position tracking
- Record both absolute and relative positions
- Use simpler method to get relative position
- Generalize getColorUnderCursor to be getColorAtCoordinate
2024-10-05 23:41:33 -04:00
psychedelicious
53443084c5 tidy(ui): move getColorUnderCursor to utils 2024-10-05 23:41:33 -04:00
psychedelicious
8d2e5bfd77 tidy(ui): use constants for keys 2024-10-05 23:41:33 -04:00
psychedelicious
05e285c95a tidy(ui): getCanDraw code style 2024-10-05 23:41:33 -04:00
psychedelicious
25f19a35d7 tidy(ui): use entity isInteractable in tool module 2024-10-05 23:41:33 -04:00
psychedelicious
01bbd32598 fix(ui): board drop targets
We just changed all buttons to use `onPointerUp` events to fix Apple Pencil behaviour. This, plus the specific DOM layout of boards, resulted in the `onPointerUp` being triggered on a board before the drop triggered.

The app saw this as selecting the board, which then reset the gallery selection to the first image in the board. By the time you drop, the gallery selection had reset.

DOM layout slightly altered to work around this.
2024-10-06 08:15:53 +11:00
Thomas Bolteau
0e2761d5c6 translationBot(ui): update translation (French)
Currently translated at 54.1% (796 of 1470 strings)

Co-authored-by: Thomas Bolteau <thomas.bolteau50@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/fr/
Translation: InvokeAI/Web UI
2024-10-05 15:12:51 +10:00
psychedelicious
d5b51cca56 chore: bump version to v5.1.0rc5 2024-10-04 22:17:41 -04:00
psychedelicious
a303777777 fix(ui): image context menu buttons don't close menu
Need to render as a `MenuItem` to trigger the close behaviour
2024-10-04 21:33:01 -04:00
psychedelicious
e90b3de706 feat(ui): error state for missing ip adapter image 2024-10-04 21:30:38 -04:00
psychedelicious
3ce94e5b84 feat(ui): improved node image drop target & error state 2024-10-04 21:30:38 -04:00
psychedelicious
42e5ec3916 fix(ui): fix wonky drop target layouts 2024-10-04 21:30:38 -04:00
psychedelicious
ffa00d1d9a chore(ui): lint 2024-10-05 09:47:22 +10:00
psychedelicious
1648a2af6e fix(ui): board title editable 2024-10-05 09:47:22 +10:00
psychedelicious
852e9e280a chore: bump version to v5.1.0rc4 2024-10-04 08:19:44 -04:00
Riku
af72412d3f translationBot(ui): update translation (German)
Currently translated at 66.0% (971 of 1470 strings)

Co-authored-by: Riku <riku.block@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/de/
Translation: InvokeAI/Web UI
2024-10-04 21:51:59 +10:00
psychedelicious
72f715e688 fix(ui): disable long-press context menu on canvas, add menu button 2024-10-04 07:44:40 -04:00
psychedelicious
3b567bef3d chore(ui): bump @invoke-ai/ui-library
This brings in the ability to disable long-press on context menus and a threshold move distance that cancels a pending long-press.
2024-10-04 07:44:40 -04:00
psychedelicious
3d867db315 chore(ui): bump @invoke-ai/ui-library
This brings in long-press support for context menus.
2024-10-04 07:44:40 -04:00
psychedelicious
a8c7dd74d0 fix(ui): type stuff 2024-10-04 07:44:40 -04:00
psychedelicious
2dc069d759 chore(ui): lint 2024-10-04 07:44:40 -04:00
psychedelicious
2a90f4f59e fix(ui): Apple Pencil requires onPointerUp instead of onClick
With `onClick`, elements w/ a tooltip require a double-tap.
2024-10-04 07:44:40 -04:00
psychedelicious
af5f342347 chore(ui): bump @invoke-ai/ui-library
This brings in a fix for Apple Pencil.
2024-10-04 07:44:40 -04:00
psychedelicious
6dd53b6a32 fix(ui): viewport cut off on iPad
Need to use dynamic viewport units.
2024-10-04 07:44:40 -04:00
psychedelicious
0ca8351911 fix(ui): incorrect hotkeys on floating button tooltips 2024-10-04 07:27:30 -04:00
psychedelicious
b14cbfde13 chore: v5.1.0rc3 2024-10-04 09:32:54 +10:00
psychedelicious
46dc633df9 installer: update torch extra-index-url 2024-10-04 09:32:54 +10:00
jkbdco
d4a981fc1c Update docker-compose.yml
Changed image from local (which most people looking for a boilerplate compose file likely will not have) to latest.
2024-10-04 07:21:20 +10:00
Jonseed
e0474ce822 Update communityNodes.md add Ollama node
Added an Ollama Node to the community nodes
2024-10-04 07:19:00 +10:00
41 changed files with 1814 additions and 510 deletions

View File

@@ -105,7 +105,7 @@ Invoke features an organized gallery system for easily storing, accessing, and r
### Other features
- Support for both ckpt and diffusers models
- SD1.5, SD2.0, and SDXL support
- SD1.5, SD2.0, SDXL, and FLUX support
- Upscaling Tools
- Embedding Manager & Support
- Model Manager & Support

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2023 Eugene Brodsky https://github.com/ebr
x-invokeai: &invokeai
image: "local/invokeai:latest"
image: "ghcr.io/invoke-ai/invokeai:latest"
build:
context: ..
dockerfile: docker/Dockerfile

View File

@@ -40,6 +40,7 @@ To use a community workflow, download the `.json` node graph file and load it in
+ [Metadata-Linked](#metadata-linked-nodes)
+ [Negative Image](#negative-image)
+ [Nightmare Promptgen](#nightmare-promptgen)
+ [Ollama](#ollama-node)
+ [One Button Prompt](#one-button-prompt)
+ [Oobabooga](#oobabooga)
+ [Prompt Tools](#prompt-tools)
@@ -390,6 +391,19 @@ View:
**Node Link:** [https://github.com/gogurtenjoyer/nightmare-promptgen](https://github.com/gogurtenjoyer/nightmare-promptgen)
--------------------------------
### Ollama Node
**Description:** Uses Ollama API to expand text prompts for text-to-image generation using local LLMs. Works great for expanding basic prompts into detailed natural language prompts for Flux. Also provides a toggle to unload the LLM model immediately after expanding, to free up VRAM for Invoke to continue the image generation workflow.
**Node Link:** https://github.com/Jonseed/Ollama-Node
**Example Node Graph:** https://github.com/Jonseed/Ollama-Node/blob/main/Ollama-Node-Flux-example.json
**View:**
![ollama node](https://raw.githubusercontent.com/Jonseed/Ollama-Node/a3e7cdc55e394cb89c1ea7ed54e106c212c85e8c/ollama-node-screenshot.png)
--------------------------------
### One Button Prompt

View File

@@ -421,7 +421,7 @@ def get_torch_source() -> Tuple[str | None, str | None]:
optional_modules = "[xformers,onnx-cuda]"
elif OS == "Windows":
if device.value == "cuda":
url = "https://download.pytorch.org/whl/cu121"
url = "https://download.pytorch.org/whl/cu124"
optional_modules = "[xformers,onnx-cuda]"
elif device.value == "cpu":
# CPU uses the default PyPi index, no optional modules

View File

@@ -58,7 +58,7 @@
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@fontsource-variable/inter": "^5.1.0",
"@invoke-ai/ui-library": "^0.0.37",
"@invoke-ai/ui-library": "^0.0.41",
"@nanostores/react": "^0.7.3",
"@reduxjs/toolkit": "2.2.3",
"@roarr/browser-log-writer": "^1.3.0",
@@ -83,6 +83,7 @@
"overlayscrollbars-react": "^0.5.6",
"perfect-freehand": "^1.2.2",
"query-string": "^9.1.0",
"raf-throttle": "^2.0.6",
"react": "^18.3.1",
"react-colorful": "^5.6.1",
"react-dom": "^18.3.1",

View File

@@ -24,8 +24,8 @@ dependencies:
specifier: ^5.1.0
version: 5.1.0
'@invoke-ai/ui-library':
specifier: ^0.0.37
version: 0.0.37(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.1.0)(@types/react@18.3.11)(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1)
specifier: ^0.0.41
version: 0.0.41(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.1.0)(@types/react@18.3.11)(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1)
'@nanostores/react':
specifier: ^0.7.3
version: 0.7.3(nanostores@0.11.3)(react@18.3.1)
@@ -98,6 +98,9 @@ dependencies:
query-string:
specifier: ^9.1.0
version: 9.1.0
raf-throttle:
specifier: ^2.0.6
version: 2.0.6
react:
specifier: ^18.3.1
version: 18.3.1
@@ -490,8 +493,8 @@ packages:
resolution: {integrity: sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg==}
dev: false
/@chakra-ui/anatomy@2.3.2:
resolution: {integrity: sha512-YRezCngYKigfIOLVszAL21lv1h+61pgxVGRu2rcsVGCbbvGSTkMhoML2/Yw2c03sEkrhBIVx1RtX+7550njaoA==}
/@chakra-ui/anatomy@2.3.4:
resolution: {integrity: sha512-fFIYN7L276gw0Q7/ikMMlZxP7mvnjRaWJ7f3Jsf9VtDOi6eAYIBRrhQe6+SZ0PGmoOkRaBc7gSE5oeIbgFFyrw==}
dev: false
/@chakra-ui/breakpoint-utils@2.0.8:
@@ -548,12 +551,12 @@ packages:
react: 18.3.1
dev: false
/@chakra-ui/hooks@2.3.2(react@18.3.1):
resolution: {integrity: sha512-ETe3gJYf5my2ri8WKWLZYuwn+nYzItrTKss9pG5bSPmWEWG4qWP+Zjl6hnfMI11c1dfUatpQZZqd4KsFQVNWRA==}
/@chakra-ui/hooks@2.4.1(react@18.3.1):
resolution: {integrity: sha512-f81S0TiSh14LjiXBAvdezmIQLKGR7pckQ8kLMIxjngNW4VV92dhznOP+VcQmDOyXslfWsaWNFswlASE6clVw1g==}
peerDependencies:
react: '>=18'
dependencies:
'@chakra-ui/utils': 2.1.2(react@18.3.1)
'@chakra-ui/utils': 2.2.1(react@18.3.1)
'@zag-js/element-size': 0.31.1
copy-to-clipboard: 3.3.3
framesync: 6.1.2
@@ -571,13 +574,13 @@ packages:
react: 18.3.1
dev: false
/@chakra-ui/icons@2.2.2(@chakra-ui/react@2.9.3)(react@18.3.1):
resolution: {integrity: sha512-qmQHmc9hoGaGLpSiYeKFyC9zmCFvBdOs2vkHomlXSPsDqLDZYpADFYgSeqodcYmTZhywI5Ax/EIlGEz0jdAiyA==}
/@chakra-ui/icons@2.2.4(@chakra-ui/react@2.10.1)(react@18.3.1):
resolution: {integrity: sha512-l5QdBgwrAg3Sc2BRqtNkJpfuLw/pWRDwwT58J6c4PqQT6wzXxyNa8Q0PForu1ltB5qEiFb1kxr/F/HO1EwNa6g==}
peerDependencies:
'@chakra-ui/react': '>=2.0.0'
react: '>=18'
dependencies:
'@chakra-ui/react': 2.9.3(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1)
'@chakra-ui/react': 2.10.1(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1)
react: 18.3.1
dev: false
@@ -800,8 +803,8 @@ packages:
react: 18.3.1
dev: false
/@chakra-ui/react@2.9.3(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1):
resolution: {integrity: sha512-ccg0CVgAqKtU/xb1w86+A2XC/56g8AUCNKYG0SrI0P89WGYHsM+5xHiuUgtPeIg0vuuQS8WZupm9BgiPIp67Og==}
/@chakra-ui/react@2.10.1(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1):
resolution: {integrity: sha512-YUOPyuq6Z0P1TSURNQRccwV/YbiWlkHlRLEMqNMwFQeQe7lyXfkPHn8GZyxvaiu+S02iopG9xg+wl68K4G3FSQ==}
peerDependencies:
'@emotion/react': '>=11'
'@emotion/styled': '>=11'
@@ -809,10 +812,10 @@ packages:
react: '>=18'
react-dom: '>=18'
dependencies:
'@chakra-ui/hooks': 2.3.2(react@18.3.1)
'@chakra-ui/styled-system': 2.10.2(react@18.3.1)
'@chakra-ui/theme': 3.4.2(@chakra-ui/styled-system@2.10.2)(react@18.3.1)
'@chakra-ui/utils': 2.1.2(react@18.3.1)
'@chakra-ui/hooks': 2.4.1(react@18.3.1)
'@chakra-ui/styled-system': 2.11.1(react@18.3.1)
'@chakra-ui/theme': 3.4.5(@chakra-ui/styled-system@2.11.1)(react@18.3.1)
'@chakra-ui/utils': 2.2.1(react@18.3.1)
'@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
'@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.11)(react@18.3.1)
'@popperjs/core': 2.11.8
@@ -844,10 +847,10 @@ packages:
react: 18.3.1
dev: false
/@chakra-ui/styled-system@2.10.2(react@18.3.1):
resolution: {integrity: sha512-MXV/oahBBoWroZmLqIERQE3a2cJrb0iu+Evv1km3pCw/gtA/mhV3ogEIZt0oUEblcE0Nh80FwTTRZOJhboUZXw==}
/@chakra-ui/styled-system@2.11.1(react@18.3.1):
resolution: {integrity: sha512-1O4vQSSJqDYl+xkQLgNLVsBhEUkFd5BKQAIBEZC5ppppvCXAt+BPqrOLFAkNyo7piKYp5mf6fdYBAW/ESurqeg==}
dependencies:
'@chakra-ui/utils': 2.1.2(react@18.3.1)
'@chakra-ui/utils': 2.2.1(react@18.3.1)
csstype: 3.1.3
transitivePeerDependencies:
- react
@@ -891,14 +894,14 @@ packages:
color2k: 2.0.3
dev: false
/@chakra-ui/theme-tools@2.2.2(@chakra-ui/styled-system@2.10.2)(react@18.3.1):
resolution: {integrity: sha512-iK9xoIEnEO3mXSDUsnCNigFifgRU3K8fhuIrN+q20V7YxenrifqJ7wAbbALHxy7Awrfe3NOqdVca2lnsQ2L4Ig==}
/@chakra-ui/theme-tools@2.2.5(@chakra-ui/styled-system@2.11.1)(react@18.3.1):
resolution: {integrity: sha512-tUIv04Pzm2W8v5Iugei15qREIFX2mslI3fEouIVih8vHzwY821neCfspxOsAhvYSLNX+1kzk6ArBo9WzUpqn+Q==}
peerDependencies:
'@chakra-ui/styled-system': '>=2.0.0'
dependencies:
'@chakra-ui/anatomy': 2.3.2
'@chakra-ui/styled-system': 2.10.2(react@18.3.1)
'@chakra-ui/utils': 2.1.2(react@18.3.1)
'@chakra-ui/anatomy': 2.3.4
'@chakra-ui/styled-system': 2.11.1(react@18.3.1)
'@chakra-ui/utils': 2.2.1(react@18.3.1)
color2k: 2.0.3
transitivePeerDependencies:
- react
@@ -924,15 +927,15 @@ packages:
'@chakra-ui/theme-tools': 2.1.2(@chakra-ui/styled-system@2.9.2)
dev: false
/@chakra-ui/theme@3.4.2(@chakra-ui/styled-system@2.10.2)(react@18.3.1):
resolution: {integrity: sha512-iZ9WelkjJ7VJzWCDFpiYaAxGodW8Bahz+YrGp3P/CKsQrH1yOVHE9R190H9eiiSxw7tOyniKbMdd31GI8HaYtA==}
/@chakra-ui/theme@3.4.5(@chakra-ui/styled-system@2.11.1)(react@18.3.1):
resolution: {integrity: sha512-h/00W3xFCrUnsosVdh/SNMKfaez9jo1MMz2ruZAcpg/b1Jk0WnYa/ONhUwN89DR3UrPHSR+/vigZBxaA1+3QUQ==}
peerDependencies:
'@chakra-ui/styled-system': '>=2.8.0'
dependencies:
'@chakra-ui/anatomy': 2.3.2
'@chakra-ui/styled-system': 2.10.2(react@18.3.1)
'@chakra-ui/theme-tools': 2.2.2(@chakra-ui/styled-system@2.10.2)(react@18.3.1)
'@chakra-ui/utils': 2.1.2(react@18.3.1)
'@chakra-ui/anatomy': 2.3.4
'@chakra-ui/styled-system': 2.11.1(react@18.3.1)
'@chakra-ui/theme-tools': 2.2.5(@chakra-ui/styled-system@2.11.1)(react@18.3.1)
'@chakra-ui/utils': 2.2.1(react@18.3.1)
transitivePeerDependencies:
- react
dev: false
@@ -957,8 +960,8 @@ packages:
lodash.mergewith: 4.6.2
dev: false
/@chakra-ui/utils@2.1.2(react@18.3.1):
resolution: {integrity: sha512-zByY3e1SUjNJ8jLjpOEaM2r+j6fPyh2XxPFHKDwbuzgSRrsJX+5BGozXfh8ZhmIJmSuByOWnWvGDSWxl3gCqag==}
/@chakra-ui/utils@2.2.1(react@18.3.1):
resolution: {integrity: sha512-e9D6CDtQZWBx+rq7yW13mAeI3Vod4cWfUIip+XFNjPWQSJGOJb1MiXocoiJqaZ1S+jsN+mod4uCZmGcukv0iSg==}
peerDependencies:
react: '>=16.8.0'
dependencies:
@@ -1694,20 +1697,20 @@ packages:
prettier: 3.3.3
dev: true
/@invoke-ai/ui-library@0.0.37(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.1.0)(@types/react@18.3.11)(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1):
resolution: {integrity: sha512-3jsabdg0ySu7/YwFfnFBcpG0K0oWO7G57iOTSr6/yE/BwoSMN1oN5wMlPifG3sLQs2JfXnbglh06gbHAxBXDyw==}
/@invoke-ai/ui-library@0.0.41(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.1.0)(@types/react@18.3.11)(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1):
resolution: {integrity: sha512-bV83fXxqSuEMghQ68nF6uFkPE/DXtbnY2ZiXcoXXhOvv9X32/uKWSrcaeka9lZxrr10GFvV5a5LRORDornDaGg==}
peerDependencies:
'@fontsource-variable/inter': ^5.0.16
react: ^18.2.0
react-dom: ^18.2.0
dependencies:
'@chakra-ui/anatomy': 2.3.2
'@chakra-ui/icons': 2.2.2(@chakra-ui/react@2.9.3)(react@18.3.1)
'@chakra-ui/anatomy': 2.2.2
'@chakra-ui/icons': 2.2.4(@chakra-ui/react@2.10.1)(react@18.3.1)
'@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1)
'@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1)
'@chakra-ui/react': 2.9.3(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1)
'@chakra-ui/styled-system': 2.10.2(react@18.3.1)
'@chakra-ui/theme-tools': 2.2.2(@chakra-ui/styled-system@2.10.2)(react@18.3.1)
'@chakra-ui/react': 2.10.1(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.11)(framer-motion@11.10.0)(react-dom@18.3.1)(react@18.3.1)
'@chakra-ui/styled-system': 2.9.2
'@chakra-ui/theme-tools': 2.1.2(@chakra-ui/styled-system@2.9.2)
'@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
'@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.11)(react@18.3.1)
'@fontsource-variable/inter': 5.1.0
@@ -1722,7 +1725,7 @@ packages:
react-dom: 18.3.1(react@18.3.1)
react-i18next: 15.0.2(i18next@23.15.1)(react-dom@18.3.1)(react@18.3.1)
react-icons: 5.3.0(react@18.3.1)
react-select: 5.8.1(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1)
react-select: 5.8.0(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1)
transitivePeerDependencies:
- '@chakra-ui/form-control'
- '@chakra-ui/icon'
@@ -7568,6 +7571,10 @@ packages:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
/raf-throttle@2.0.6:
resolution: {integrity: sha512-C7W6hy78A+vMmk5a/B6C5szjBHrUzWJkVyakjKCK59Uy2CcA7KhO1JUvvH32IXYFIcyJ3FMKP3ZzCc2/71I6Vg==}
dev: false
/railroad-diagrams@1.0.0:
resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==}
dev: false
@@ -7884,28 +7891,6 @@ packages:
- supports-color
dev: false
/react-select@5.8.1(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1):
resolution: {integrity: sha512-RT1CJmuc+ejqm5MPgzyZujqDskdvB9a9ZqrdnVLsvAHjJ3Tj0hELnLeVPQlmYdVKCdCpxanepl6z7R5KhXhWzg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
'@babel/runtime': 7.25.7
'@emotion/cache': 11.13.1
'@emotion/react': 11.13.3(@types/react@18.3.11)(react@18.3.1)
'@floating-ui/dom': 1.6.11
'@types/react-transition-group': 4.4.11
memoize-one: 6.0.0
prop-types: 15.8.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1)
use-isomorphic-layout-effect: 1.1.2(@types/react@18.3.11)(react@18.3.1)
transitivePeerDependencies:
- '@types/react'
- supports-color
dev: false
/react-style-singleton@2.2.1(@types/react@18.3.11)(react@18.3.1):
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
engines: {node: '>=10'}

View File

@@ -651,7 +651,8 @@
"imageCopied": "Bild kopiert",
"parametersNotSet": "Parameter nicht festgelegt",
"addedToBoard": "Dem Board hinzugefügt",
"loadedWithWarnings": "Workflow mit Warnungen geladen"
"loadedWithWarnings": "Workflow mit Warnungen geladen",
"imageSaved": "Bild gespeichert"
},
"accessibility": {
"uploadImage": "Bild hochladen",
@@ -664,7 +665,9 @@
"resetUI": "$t(accessibility.reset) von UI",
"createIssue": "Ticket erstellen",
"about": "Über",
"submitSupportTicket": "Support-Ticket senden"
"submitSupportTicket": "Support-Ticket senden",
"toggleRightPanel": "Rechtes Bedienfeld umschalten (G)",
"toggleLeftPanel": "Linkes Bedienfeld umschalten (T)"
},
"boards": {
"autoAddBoard": "Board automatisch erstellen",
@@ -1062,7 +1065,22 @@
"missingFieldTemplate": "Fehlende Feldvorlage",
"missingNode": "Fehlender Aufrufknoten",
"missingInvocationTemplate": "Fehlende Aufrufvorlage",
"edit": "Bearbeiten"
"edit": "Bearbeiten",
"workflowAuthor": "Autor",
"graph": "Graph",
"workflowDescription": "Kurze Beschreibung",
"versionUnknown": " Version unbekannt",
"workflow": "Arbeitsablauf",
"noGraph": "Kein Graph",
"version": "Version",
"zoomInNodes": "Hineinzoomen",
"zoomOutNodes": "Herauszoomen",
"workflowName": "Name",
"unknownNode": "Unbekannter Knoten",
"workflowContact": "Kontaktdaten",
"workflowNotes": "Notizen",
"workflowTags": "Tags",
"workflowVersion": "Version"
},
"hrf": {
"enableHrf": "Korrektur für hohe Auflösungen",
@@ -1304,7 +1322,28 @@
"mergeVisibleError": "Fehler beim Vereinen sichtbarer Ebenen",
"clearHistory": "Verlauf leeren",
"addLayer": "Ebene hinzufügen",
"width": "Breite"
"width": "Breite",
"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",
"addNegativePrompt": "$t(controlLayers.negativePrompt) hinzufügen",
"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"
}
},
"upsell": {
"shareAccess": "Zugang teilen",

View File

@@ -10,8 +10,8 @@
"previousImage": "Previous Image",
"reset": "Reset",
"resetUI": "$t(accessibility.reset) UI",
"toggleRightPanel": "Toggle Right Panel (T)",
"toggleLeftPanel": "Toggle Left Panel (G)",
"toggleRightPanel": "Toggle Right Panel (G)",
"toggleLeftPanel": "Toggle Left Panel (T)",
"uploadImage": "Upload Image"
},
"boards": {
@@ -53,7 +53,8 @@
"imagesWithCount_one": "{{count}} image",
"imagesWithCount_other": "{{count}} images",
"assetsWithCount_one": "{{count}} asset",
"assetsWithCount_other": "{{count}} assets"
"assetsWithCount_other": "{{count}} assets",
"updateBoardError": "Error updating board"
},
"accordions": {
"generation": {
@@ -1651,7 +1652,6 @@
"rasterLayers_withCount_visible": "Raster Layers ({{count}})",
"globalReferenceImages_withCount_visible": "Global Reference Images ({{count}})",
"inpaintMasks_withCount_visible": "Inpaint Masks ({{count}})",
"layer": "Layer",
"layer_one": "Layer",
"layer_other": "Layers",
"layer_withCount_one": "Layer ({{count}})",

View File

@@ -207,7 +207,9 @@
"settings": "Paramètres",
"predictionType": "Type de Prédiction",
"advanced": "Avancé",
"modelType": "Type de modèle"
"modelType": "Type de modèle",
"vaePrecision": "Précision VAE",
"noModelSelected": "Aucun modèle sélectionné"
},
"parameters": {
"images": "Images",
@@ -268,7 +270,16 @@
"cancel": "Annuler"
},
"useCpuNoise": "Utiliser le bruit du CPU",
"imageActions": "Actions d'image"
"imageActions": "Actions d'image",
"setToOptimalSize": "Optimiser la taille pour le modèle",
"setToOptimalSizeTooSmall": "$t(parameters.setToOptimalSize) (peut être trop petit)",
"swapDimensions": "Échanger les dimensions",
"aspect": "Aspect",
"cfgRescaleMultiplier": "Multiplicateur de mise à l'échelle CFG",
"setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (peut être trop grand)",
"useSize": "Utiliser la taille",
"remixImage": "Remixer l'image",
"lockAspectRatio": "Verrouiller le rapport hauteur/largeur"
},
"settings": {
"models": "Modèles",
@@ -284,7 +295,23 @@
"beta": "Bêta",
"generation": "Génération",
"ui": "Interface Utilisateur",
"developer": "Développeur"
"developer": "Développeur",
"enableNSFWChecker": "Activer le vérificateur NSFW",
"clearIntermediatesDesc2": "Les images intermédiaires sont des sous-produits de la génération, différentes des images de résultat dans la galerie. La suppression des intermédiaires libérera de l'espace disque.",
"clearIntermediatesDisabled": "La file d'attente doit être vide pour effacer les intermédiaires.",
"reloadingIn": "Rechargement dans",
"intermediatesClearedFailed": "Problème de suppression des intermédiaires",
"clearIntermediates": "Effacer les intermédiaires",
"enableInvisibleWatermark": "Activer le Filigrane Invisible",
"clearIntermediatesDesc1": "Effacer les intermédiaires réinitialisera votre Toile et votre ControlNet.",
"enableInformationalPopovers": "Activer les infobulles d'information",
"intermediatesCleared_one": "Effacé {{count}} Intermédiaire",
"intermediatesCleared_many": "Effacé {{count}} Intermédiaires",
"intermediatesCleared_other": "Effacé {{count}} Intermédiaires",
"clearIntermediatesDesc3": "Vos images de galerie ne seront pas supprimées.",
"clearIntermediatesWithCount_one": "Effacé {{count}} Intermédiaire",
"clearIntermediatesWithCount_many": "Effacé {{count}} Intermédiaires",
"clearIntermediatesWithCount_other": "Effacé {{count}} Intermédiaires"
},
"toast": {
"uploadFailed": "Téléchargement échoué",
@@ -304,7 +331,15 @@
"loadedWithWarnings": "Processus chargé avec des avertissements",
"imageUploaded": "Image importée",
"modelAddedSimple": "Modèle ajouté à la file d'attente",
"setControlImage": "Définir comme image de contrôle"
"setControlImage": "Définir comme image de contrôle",
"workflowDeleted": "Processus 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": "Téléchargement invalide",
"problemDownloadingImage": "Impossible de télécharger l'image",
"problemRetrievingWorkflow": "Problème de récupération du processus",
"problemDeletingWorkflow": "Problème de suppression du processus"
},
"accessibility": {
"uploadImage": "Charger une image",
@@ -630,7 +665,11 @@
"heading": "Prompt Négatif"
},
"paramVAEPrecision": {
"heading": "Précision du VAE"
"heading": "Précision du VAE",
"paragraphs": [
"La précision utilisée lors de l'encodage et du décodage VAE.",
"La pr'ecision Fp16/Half est plus efficace, au détriment de légères variations d'image."
]
},
"controlNetWeight": {
"heading": "Poids",
@@ -660,7 +699,8 @@
"paramScheduler": {
"heading": "Planificateur",
"paragraphs": [
"Planificateur utilisé pendant le processus de génération."
"Planificateur utilisé pendant le processus de génération.",
"Chaque planificateur définit comment ajouter de manière itérative du bruit à une image ou comment mettre à jour un échantillon en fonction de la sortie d'un modèle."
]
},
"controlNet": {
@@ -670,7 +710,11 @@
"heading": "ControlNet"
},
"paramSteps": {
"heading": "Étapes"
"heading": "Étapes",
"paragraphs": [
"Nombre d'étapes qui seront effectuées à chaque génération.",
"Des nombres d'étapes plus élevés créeront généralement de meilleures images, mais nécessiteront plus de temps de génération."
]
},
"controlNetBeginEnd": {
"heading": "Pourcentage de début / de fin d'étape",
@@ -695,7 +739,10 @@
]
},
"paramVAE": {
"heading": "VAE"
"heading": "VAE",
"paragraphs": [
"Modèle utilisé pour convertir la sortie de l'IA en l'image finale."
]
},
"compositingCoherenceMode": {
"heading": "Mode",
@@ -704,7 +751,11 @@
]
},
"paramIterations": {
"heading": "Itérations"
"heading": "Itérations",
"paragraphs": [
"Le nombre d'images à générer.",
"Si les prompts dynamiques sont activées, chaque prompt sera généré autant de fois."
]
},
"dynamicPrompts": {
"paragraphs": [
@@ -715,7 +766,10 @@
"heading": "Prompts Dynamiques"
},
"paramModel": {
"heading": "Modèle"
"heading": "Modèle",
"paragraphs": [
"Modèle utilisé pour la génération. Différents modèles sont entraînés pour se spécialiser dans la production de résultats esthétiques et de contenus variés."
]
},
"compositingCoherencePass": {
"heading": "Passe de cohérence",
@@ -724,13 +778,24 @@
]
},
"paramRatio": {
"heading": "Rapport hauteur/largeur"
"heading": "Rapport hauteur/largeur",
"paragraphs": [
"Le rapport hauteur/largeur de l'image générée.",
"Une taille d'image (en nombre de pixels) équivalente à 512x512 est recommandée pour les modèles SD1.5 et une taille équivalente à 1024x1024 est recommandée pour les modèles SDXL."
]
},
"paramSeed": {
"heading": "Graine"
"heading": "Graine",
"paragraphs": [
"Contrôle le bruit de départ utilisé pour la génération.",
"Désactivez l'option \"Aléatoire\" pour produire des résultats identiques avec les mêmes paramètres de génération."
]
},
"scaleBeforeProcessing": {
"heading": "Échelle avant traitement"
"heading": "Échelle avant traitement",
"paragraphs": [
"\"Auto\" ajuste la zone sélectionnée à la taille la mieux adaptée au modèle avant le processus de génération d'image."
]
},
"compositingBlurMethod": {
"heading": "Méthode de flou",
@@ -751,7 +816,11 @@
]
},
"paramDenoisingStrength": {
"heading": "Force de débruitage"
"heading": "Force de débruitage",
"paragraphs": [
"Intensité du bruit ajouté à l'image d'entrée.",
"0 produira une image identique, tandis que 1 produira une image complètement différente."
]
},
"lora": {
"heading": "LoRA",
@@ -760,10 +829,89 @@
]
},
"noiseUseCPU": {
"heading": "Utiliser le bruit du CPU"
"heading": "Utiliser le bruit du CPU",
"paragraphs": [
"Contrôle si le bruit est généré sur le CPU ou le GPU.",
"Avec le bruit du CPU activé, une graine particulière produira la même image sur n'importe quelle machine.",
"Il n'y a aucun impact sur les performances à activer le bruit du CPU."
]
},
"paramCFGScale": {
"heading": "Échelle CFG"
"heading": "Échelle CFG",
"paragraphs": [
"Contrôle de l'influence du prompt sur le processus de génération.",
"Des valeurs élevées de l'échelle CFG peuvent entraîner une saturation excessive et des distortions. "
]
},
"loraWeight": {
"heading": "Poids",
"paragraphs": [
"Poids du LoRA. Un poids plus élevé aura un impact plus important sur l'image finale."
]
},
"imageFit": {
"heading": "Ajuster l'image initiale à la taille de sortie",
"paragraphs": [
"Redimensionne l'image initiale à la largeur et à la hauteur de l'image de sortie. Il est recommandé de l'activer."
]
},
"paramCFGRescaleMultiplier": {
"heading": "Multiplicateur de mise à l'échelle CFG",
"paragraphs": [
"Multiplicateur de mise à l'échelle pour le guidage CFG, utilisé pour les modèles entraînés en utilisant le zero-terminal SNR (ztsnr).",
"Une valeur de 0.7 est suggérée pour ces modèles."
]
},
"controlNetProcessor": {
"heading": "Processeur",
"paragraphs": [
"Méthode de traitement de l'image d'entrée pour guider le processus de génération. Différents processeurs fourniront différents effets ou styles dans vos images générées."
]
},
"paramUpscaleMethod": {
"paragraphs": [
"Méthode utilisée pour améliorer l'image pour la correction de haute résolution."
],
"heading": "Méthode d'agrandissement"
},
"refinerModel": {
"heading": "Modèle de Raffinage",
"paragraphs": [
"Modèle utilisé pendant la partie raffinage du processus de génération.",
"Similaire au Modèle de Génération."
]
},
"paramWidth": {
"paragraphs": [
"Largeur de l'image générée. Doit être un multiple de 8."
],
"heading": "Largeur"
},
"paramHeight": {
"heading": "Hauteur",
"paragraphs": [
"Hauteur de l'image générée. Doit être un multiple de 8."
]
},
"paramHrf": {
"heading": "Activer la correction haute résolution",
"paragraphs": [
"Générez des images de haute qualité à une résolution plus grande que celle qui est optimale pour le modèle. Cela est généralement utilisé pour prévenir la duplication dans l'image générée."
]
},
"patchmatchDownScaleSize": {
"paragraphs": [
"Intensité du sous-échantillonage qui se produit avant le remplissage?",
"Un sous-échantillonage plus élevé améliorera les performances et réduira la qualité."
],
"heading": "Sous-échantillonage"
},
"paramAspect": {
"paragraphs": [
"Rapport hauteur/largeur de l'image générée. Changer le rapport mettra à jour la largeur et la hauteur en conséquence.",
"\"Optimiser\" définira la largeur et la hauteur aux dimensions optimales pour le modèle choisi."
],
"heading": "Aspect"
}
},
"dynamicPrompts": {
@@ -800,7 +948,11 @@
"generationMode": "Mode Génération",
"height": "Hauteur",
"createdBy": "Créé par",
"strength": "Force d'image à image"
"strength": "Force d'image à image",
"vae": "VAE",
"noRecallParameters": "Aucun paramètre à rappeler trouvé.",
"cfgRescaleMultiplier": "$t(parameters.cfgRescaleMultiplier)",
"recallParameters": "Rappeler les paramètres"
},
"sdxl": {
"freePromptStyle": "Écriture de Prompt manuelle",
@@ -830,9 +982,9 @@
"downloadWorkflow": "Télécharger processus en JSON",
"loadWorkflow": "Charger le processus",
"reloadNodeTemplates": "Recharger les modèles de nœuds",
"animatedEdges": "Bords animés",
"animatedEdges": "Connexions animées",
"cannotConnectToSelf": "Impossible de se connecter à soi-même",
"edge": "Bord",
"edge": "Connexion",
"workflowAuthor": "Auteur",
"enum": "Énumération",
"integer": "Entier",
@@ -844,8 +996,8 @@
"version": "Version",
"boolean": "Booléens",
"executionStateCompleted": "Terminé",
"colorCodeEdges": "Code de couleur des bords",
"colorCodeEdgesHelp": "Code couleur des arêtes en fonction de leurs champs connectés.",
"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",
@@ -874,7 +1026,7 @@
"unknownField": "Champ inconnu",
"workflowNotes": "Notes",
"workflowTags": "Tags",
"animatedEdgesHelp": "Animer les arêtes sélectionnées et les arêtes connectées aux nœuds sélectionnés.",
"animatedEdgesHelp": "Animer les connexions sélectionnées et les connexions associées aux nœuds sélectionnés",
"nodeTemplate": "Modèle de nœud",
"fieldTypesMustMatch": "Les types de champs doivent correspondre.",
"fullyContainNodes": "Contient complètement les nœuds à sélectionner",
@@ -897,13 +1049,86 @@
"workflowName": "Nom",
"snapToGridHelp": "Aligner les nœuds sur la grille lorsqu'ils sont déplacés.",
"unableToValidateWorkflow": "Impossible de valider le processus",
"validateConnections": "Valider les connexions et le graphique"
"validateConnections": "Valider les connexions et le graphique",
"unableToUpdateNodes_one": "Impossible de mettre à jour {{count}} nœud",
"unableToUpdateNodes_many": "Impossible de mettre à jour {{count}} nœuds",
"unableToUpdateNodes_other": "Impossible de mettre à jour {{count}} nœuds",
"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.",
"sourceNodeFieldDoesNotExist": "Connexion invalide : {{node}}.{{field}} n'existe pas",
"unableToGetWorkflowVersion": "Impossible d'obtenir la version du schéma de processus",
"newWorkflowDesc2": "Votre processus actuel comporte des modifications non enregistrées.",
"deletedInvalidEdge": "Connexion invalide supprimé {{source}} -> {{target}}",
"targetNodeDoesNotExist": "Connexion invalide : le nœud cible/entrée {{node}} n'existe pas.",
"targetNodeFieldDoesNotExist": "Connexion invalide : le champ {{node}}.{{field}} n'existe pas.",
"nodeVersion": "Version du noeud",
"clearWorkflowDesc2": "Votre processus actuel comporte des modifications non enregistrées.",
"clearWorkflow": "Effacer le Processus",
"clearWorkflowDesc": "Effacer ce processus 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}})",
"ipAdapter": "IP-Adapter",
"viewMode": "Utiliser en vue linéaire",
"collectionFieldType": "{{name}} (Collection)",
"newWorkflow": "Nouveau processus",
"reorderLinearView": "Réorganiser la vue linéaire",
"unknownOutput": "Sortie inconnue : {{name}}",
"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",
"betaDesc": "Cette invocation est en version bêta. Tant qu'elle n'est pas stable, elle peut avoir des changements majeurs lors des mises à jour de l'application. Nous prévoyons de soutenir cette invocation à long terme.",
"unknownFieldType": "$t(nodes.unknownField) type : {{type}}",
"inputFieldTypeParseError": "Impossible d'analyser le type du champ d'entrée {{node}}.{{field}} ({{message}})",
"unableToExtractSchemaNameFromRef": "impossible d'extraire le nom du schéma à partir de la référence",
"editMode": "Modifier dans l'éditeur de processus",
"unknownErrorValidatingWorkflow": "Erreur inconnue lors de la validation du processus.",
"updateAllNodes": "Mettre à jour les nœuds",
"allNodesUpdated": "Tous les nœuds mis à jour",
"newWorkflowDesc": "Créer un nouveau processus?"
},
"models": {
"noMatchingModels": "Aucun modèle correspondant",
"noModelsAvailable": "Aucun modèle disponible",
"loading": "chargement",
"selectModel": "Sélectionner un modèle",
"noMatchingLoRAs": "Aucun LoRA correspondant"
"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"
},
"workflows": {
"workflowLibrary": "Bibliothèque",
"loading": "Chargement des processus",
"searchWorkflows": "Rechercher des processus",
"workflowCleared": "Processus effacé",
"noDescription": "Aucune description",
"deleteWorkflow": "Supprimer le processus",
"openWorkflow": "Ouvrir le processus",
"uploadWorkflow": "Charger à partir du fichier",
"workflowName": "Nom du processus",
"unnamedWorkflow": "Processus sans nom",
"saveWorkflowAs": "Enregistrer le processus sous",
"workflows": "Processus",
"savingWorkflow": "Enregistrement du processus...",
"saveWorkflowToProject": "Enregistrer le processus dans le projet",
"downloadWorkflow": "Enregistrer dans le fichier",
"saveWorkflow": "Enregistrer le processus",
"problemSavingWorkflow": "Problème de sauvegarde du processus",
"workflowEditorMenu": "Menu de l'Éditeur de Processus",
"newWorkflowCreated": "Nouveau processus créé",
"clearWorkflowSearchFilter": "Réinitialiser le filtre de recherche de processus",
"problemLoading": "Problème de chargement des processus",
"workflowSaved": "Processus enregistré",
"noWorkflows": "Pas de processus"
}
}

View File

@@ -65,7 +65,7 @@
"blue": "Blu",
"alpha": "Alfa",
"copy": "Copia",
"on": "Attivato",
"on": "Acceso",
"checkpoint": "Checkpoint",
"safetensors": "Safetensors",
"ai": "ia",
@@ -85,7 +85,7 @@
"openInViewer": "Apri nel visualizzatore",
"apply": "Applica",
"loadingImage": "Caricamento immagine",
"off": "Disattivato",
"off": "Spento",
"edit": "Modifica",
"placeholderSelectAModel": "Seleziona un modello",
"reset": "Reimposta",
@@ -321,6 +321,22 @@
"selectViewTool": {
"title": "Strumento Visualizza",
"desc": "Seleziona lo strumento Visualizza."
},
"applyFilter": {
"title": "Applica filtro",
"desc": "Applica il filtro in sospeso al livello selezionato."
},
"cancelFilter": {
"title": "Annulla filtro",
"desc": "Annulla il filtro in sospeso."
},
"cancelTransform": {
"desc": "Annulla la trasformazione in sospeso.",
"title": "Annulla Trasforma"
},
"applyTransform": {
"title": "Applica trasformazione",
"desc": "Applica la trasformazione in sospeso al livello selezionato."
}
},
"workflows": {
@@ -574,8 +590,8 @@
"scale": "Scala",
"imageFit": "Adatta l'immagine iniziale alle dimensioni di output",
"scaleBeforeProcessing": "Scala prima dell'elaborazione",
"scaledWidth": "Larghezza ridimensionata",
"scaledHeight": "Altezza ridimensionata",
"scaledWidth": "Larghezza scalata",
"scaledHeight": "Altezza scalata",
"infillMethod": "Metodo di riempimento",
"tileSize": "Dimensione piastrella",
"downloadImage": "Scarica l'immagine",
@@ -617,7 +633,11 @@
"ipAdapterIncompatibleBaseModel": "Il modello base dell'adattatore IP non è compatibile",
"ipAdapterNoImageSelected": "Nessuna immagine dell'adattatore IP selezionata",
"rgNoPromptsOrIPAdapters": "Nessun prompt o adattatore IP",
"rgNoRegion": "Nessuna regione selezionata"
"rgNoRegion": "Nessuna regione selezionata",
"t2iAdapterIncompatibleBboxWidth": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, larghezza riquadro è {{width}}",
"t2iAdapterIncompatibleBboxHeight": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, altezza riquadro è {{height}}",
"t2iAdapterIncompatibleScaledBboxWidth": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, larghezza del riquadro scalato {{width}}",
"t2iAdapterIncompatibleScaledBboxHeight": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, altezza del riquadro scalato {{height}}"
},
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), altezza riquadro è {{height}}",
"fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), larghezza riquadro è {{width}}",
@@ -625,7 +645,11 @@
"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"
"noFLUXVAEModelSelected": "Nessun modello VAE selezionato per la generazione con FLUX",
"canvasIsTransforming": "La tela sta trasformando",
"canvasIsRasterizing": "La tela sta rasterizzando",
"canvasIsCompositing": "La tela è in fase di composizione",
"canvasIsFiltering": "La tela sta filtrando"
},
"useCpuNoise": "Usa la CPU per generare rumore",
"iterations": "Iterazioni",
@@ -644,7 +668,12 @@
"processImage": "Elabora Immagine",
"sendToUpscale": "Invia a Amplia",
"postProcessing": "Post-elaborazione (Shift + U)",
"guidance": "Guida"
"guidance": "Guida",
"gaussianBlur": "Sfocatura Gaussiana",
"boxBlur": "Sfocatura Box",
"staged": "Maschera espansa",
"optimizedImageToImage": "Immagine-a-immagine ottimizzata",
"sendToCanvas": "Invia alla Tela"
},
"settings": {
"models": "Modelli",
@@ -678,7 +707,8 @@
"enableInformationalPopovers": "Abilita testo informativo a comparsa",
"reloadingIn": "Ricaricando in",
"informationalPopoversDisabled": "Testo informativo a comparsa disabilitato",
"informationalPopoversDisabledDesc": "I testi informativi a comparsa sono disabilitati. Attivali nelle impostazioni."
"informationalPopoversDisabledDesc": "I testi informativi a comparsa sono disabilitati. Attivali nelle impostazioni.",
"confirmOnNewSession": "Conferma su nuova sessione"
},
"toast": {
"uploadFailed": "Caricamento fallito",
@@ -721,7 +751,20 @@
"somethingWentWrong": "Qualcosa è andato storto",
"outOfMemoryErrorDesc": "Le impostazioni della generazione attuale superano la capacità del sistema. Modifica le impostazioni e riprova.",
"importFailed": "Importazione non riuscita",
"importSuccessful": "Importazione riuscita"
"importSuccessful": "Importazione riuscita",
"layerSavedToAssets": "Livello salvato nelle risorse",
"problemSavingLayer": "Impossibile salvare il livello",
"unableToLoadImage": "Impossibile caricare l'immagine",
"problemCopyingLayer": "Impossibile copiare il livello",
"sentToCanvas": "Inviato alla Tela",
"sentToUpscale": "Inviato a Amplia",
"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"
},
"accessibility": {
"invokeProgressBar": "Barra di avanzamento generazione",
@@ -734,7 +777,9 @@
"resetUI": "$t(accessibility.reset) l'Interfaccia Utente",
"createIssue": "Segnala un problema",
"about": "Informazioni",
"submitSupportTicket": "Invia ticket di supporto"
"submitSupportTicket": "Invia ticket di supporto",
"toggleLeftPanel": "Attiva/disattiva il pannello sinistro (T)",
"toggleRightPanel": "Attiva/disattiva il pannello destro (G)"
},
"nodes": {
"zoomOutNodes": "Rimpicciolire",
@@ -916,7 +961,8 @@
"noBoards": "Nessuna bacheca {{boardType}}",
"hideBoards": "Nascondi bacheche",
"viewBoards": "Visualizza bacheche",
"deletedPrivateBoardsCannotbeRestored": "Le bacheche cancellate non possono essere ripristinate. Selezionando 'Cancella solo bacheca', le immagini verranno spostate nella bacheca \"Non categorizzato\" privata dell'autore dell'immagine."
"deletedPrivateBoardsCannotbeRestored": "Le bacheche cancellate non possono essere ripristinate. Selezionando 'Cancella solo bacheca', le immagini verranno spostate nella bacheca \"Non categorizzato\" privata dell'autore dell'immagine.",
"updateBoardError": "Errore durante l'aggiornamento della bacheca"
},
"queue": {
"queueFront": "Aggiungi all'inizio della coda",
@@ -1401,6 +1447,25 @@
"paragraphs": [
"La struttura determina quanto l'immagine finale rispecchierà il layout dell'originale. Una struttura bassa permette cambiamenti significativi, mentre una struttura alta conserva la composizione e il layout originali."
]
},
"fluxDevLicense": {
"heading": "Licenza non commerciale",
"paragraphs": [
"I modelli FLUX.1 [dev] sono concessi in licenza con la licenza non commerciale FLUX [dev]. Per utilizzare questo tipo di modello per scopi commerciali in Invoke, visita il nostro sito Web per saperne di più."
]
},
"optimizedDenoising": {
"heading": "Immagine-a-immagine ottimizzata",
"paragraphs": [
"Abilita 'Immagine-a-immagine ottimizzata' per una scala di riduzione del rumore più graduale per le trasformazioni da immagine a immagine e di inpainting con modelli Flux. Questa impostazione migliora la capacità di controllare la quantità di modifica applicata a un'immagine, ma può essere disattivata se preferisci usare la scala di riduzione rumore standard. Questa impostazione è ancora in fase di messa a punto ed è in stato beta."
]
},
"paramGuidance": {
"heading": "Guida",
"paragraphs": [
"Controlla quanto il prompt influenza il processo di generazione.",
"Valori di guida elevati possono causare sovrasaturazione e una guida elevata o bassa può causare risultati di generazione distorti. La guida si applica solo ai modelli FLUX DEV."
]
}
},
"sdxl": {
@@ -1494,7 +1559,10 @@
"convertGraph": "Converti grafico",
"loadWorkflow": "$t(common.load) Flusso di lavoro",
"autoLayout": "Disposizione automatica",
"loadFromGraph": "Carica il flusso di lavoro dal grafico"
"loadFromGraph": "Carica il flusso di lavoro dal grafico",
"userWorkflows": "Flussi di lavoro utente",
"projectWorkflows": "Flussi di lavoro del progetto",
"defaultWorkflows": "Flussi di lavoro predefiniti"
},
"accordions": {
"compositing": {
@@ -1533,7 +1601,300 @@
"addPositivePrompt": "Aggiungi $t(controlLayers.prompt)",
"addNegativePrompt": "Aggiungi $t(controlLayers.negativePrompt)",
"regionalGuidance": "Guida regionale",
"opacity": "Opacità"
"opacity": "Opacità",
"mergeVisible": "Fondi il visibile",
"mergeVisibleOk": "Livelli visibili uniti",
"deleteReferenceImage": "Elimina l'immagine di riferimento",
"referenceImage": "Immagine di riferimento",
"fitBboxToLayers": "Adatta il riquadro di delimitazione ai livelli",
"mergeVisibleError": "Errore durante l'unione dei livelli visibili",
"regionalReferenceImage": "Immagine di riferimento Regionale",
"newLayerFromImage": "Nuovo livello da immagine",
"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",
"rasterLayer_withCount_other": "Livelli Raster",
"controlLayer_withCount_one": "$t(controlLayers.controlLayer)",
"controlLayer_withCount_many": "Livelli di controllo",
"controlLayer_withCount_other": "Livelli di controllo",
"clipToBbox": "Ritaglia i tratti al riquadro",
"duplicate": "Duplica",
"width": "Larghezza",
"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",
"recalculateRects": "Ricalcola rettangoli",
"removeBookmark": "Rimuovi segnalibro",
"saveCanvasToGallery": "Salva la tela nella Galleria",
"regional": "Regionale",
"global": "Globale",
"canvas": "Tela",
"bookmark": "Segnalibro per cambio rapido",
"newRegionalReferenceImageOk": "Immagine di riferimento regionale creata",
"newRegionalReferenceImageError": "Problema nella creazione dell'immagine di riferimento regionale",
"newControlLayerOk": "Livello di controllo creato",
"bboxOverlay": "Mostra sovrapposizione riquadro",
"resetCanvas": "Reimposta la tela",
"outputOnlyMaskedRegions": "Solo regioni mascherate in uscita",
"enableAutoNegative": "Abilita Auto Negativo",
"disableAutoNegative": "Disabilita Auto Negativo",
"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",
"rasterLayer": "Livello Raster",
"regionalGuidance_withCount_one": "$t(controlLayers.regionalGuidance)",
"regionalGuidance_withCount_many": "Guide regionali",
"regionalGuidance_withCount_other": "Guide regionali",
"inpaintMask_withCount_one": "$t(controlLayers.inpaintMask)",
"inpaintMask_withCount_many": "Maschere Inpaint",
"inpaintMask_withCount_other": "Maschere Inpaint",
"savedToGalleryOk": "Salvato nella Galleria",
"newGlobalReferenceImageOk": "Immagine di riferimento globale creata",
"newGlobalReferenceImageError": "Problema nella creazione dell'immagine di riferimento globale",
"newControlLayerError": "Problema nella creazione del livello di controllo",
"newRasterLayerOk": "Livello raster creato",
"newRasterLayerError": "Problema nella creazione del livello raster",
"saveLayerToAssets": "Salva il livello nelle Risorse",
"pullBboxIntoLayerError": "Problema nel caricare il riquadro nel livello",
"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",
"controlMode": "Modalità di controllo",
"prompt": "Prompt",
"control": "Controllo",
"megaControl": "Mega Controllo"
},
"negativePrompt": "Prompt Negativo",
"prompt": "Prompt Positivo",
"beginEndStepPercentShort": "Inizio/Fine %",
"stagingOnCanvas": "Genera immagini nella",
"ipAdapterMethod": {
"full": "Completo",
"style": "Solo Stile",
"composition": "Solo Composizione",
"ipAdapterMethod": "Metodo Adattatore IP"
},
"showingType": "Mostrare {{type}}",
"dynamicGrid": "Griglia dinamica",
"tool": {
"view": "Muovi",
"colorPicker": "Selettore Colore",
"rectangle": "Rettangolo",
"bbox": "Riquadro di delimitazione",
"move": "Sposta",
"brush": "Pennello",
"eraser": "Cancellino"
},
"filter": {
"apply": "Applica",
"reset": "Reimposta",
"process": "Elabora",
"cancel": "Annulla",
"autoProcess": "Processo automatico",
"filterType": "Tipo Filtro",
"filter": "Filtro",
"filters": "Filtri",
"mlsd_detection": {
"score_threshold": "Soglia di punteggio",
"distance_threshold": "Soglia di distanza",
"description": "Genera una mappa dei segmenti di linea dal livello selezionato utilizzando il modello di rilevamento dei segmenti di linea MLSD."
},
"content_shuffle": {
"label": "Mescola contenuto",
"scale_factor": "Fattore di scala",
"description": "Mescola il contenuto del livello selezionato, in modo simile all'effetto \"liquefa\"."
},
"mediapipe_face_detection": {
"min_confidence": "Confidenza minima",
"label": "Rilevamento del volto MediaPipe",
"max_faces": "Max volti",
"description": "Rileva i volti nel livello selezionato utilizzando il modello di rilevamento dei volti MediaPipe."
},
"dw_openpose_detection": {
"draw_face": "Disegna il volto",
"description": "Rileva le pose umane nel livello selezionato utilizzando il modello DW Openpose.",
"label": "Rilevamento DW Openpose",
"draw_hands": "Disegna le mani",
"draw_body": "Disegna il corpo"
},
"normal_map": {
"description": "Genera una mappa delle normali dal livello selezionato.",
"label": "Mappa delle normali"
},
"lineart_edge_detection": {
"label": "Rilevamento bordi Lineart",
"coarse": "Grossolano",
"description": "Genera una mappa dei bordi dal livello selezionato utilizzando il modello di rilevamento dei bordi Lineart."
},
"depth_anything_depth_estimation": {
"model_size_small": "Piccolo",
"model_size_small_v2": "Piccolo v2",
"model_size": "Dimensioni modello",
"model_size_large": "Grande",
"model_size_base": "Base",
"description": "Genera una mappa di profondità dal livello selezionato utilizzando un modello Depth Anything."
},
"color_map": {
"label": "Mappa colore",
"description": "Crea una mappa dei colori dal livello selezionato.",
"tile_size": "Dimens. Piastrella"
},
"canny_edge_detection": {
"high_threshold": "Soglia superiore",
"low_threshold": "Soglia inferiore",
"description": "Genera una mappa dei bordi dal livello selezionato utilizzando l'algoritmo di rilevamento dei bordi Canny.",
"label": "Rilevamento bordi Canny"
},
"spandrel_filter": {
"scale": "Scala di destinazione",
"autoScaleDesc": "Il modello selezionato verrà eseguito fino al raggiungimento della scala di destinazione.",
"description": "Esegue un modello immagine-a-immagine sul livello selezionato.",
"label": "Modello Immagine-a-Immagine",
"model": "Modello",
"autoScale": "Auto Scala"
},
"pidi_edge_detection": {
"quantize_edges": "Quantizza i bordi",
"scribble": "Scarabocchio",
"description": "Genera una mappa dei bordi dal livello selezionato utilizzando il modello di rilevamento dei bordi PiDiNet.",
"label": "Rilevamento bordi PiDiNet"
},
"hed_edge_detection": {
"label": "Rilevamento bordi HED",
"description": "Genera una mappa dei bordi dal livello selezionato utilizzando il modello di rilevamento dei bordi HED.",
"scribble": "Scarabocchio"
},
"lineart_anime_edge_detection": {
"description": "Genera una mappa dei bordi dal livello selezionato utilizzando il modello di rilevamento dei bordi Lineart Anime.",
"label": "Rilevamento bordi Lineart Anime"
}
},
"controlLayers_withCount_hidden": "Livelli di controllo ({{count}} nascosti)",
"regionalGuidance_withCount_hidden": "Guida regionale ({{count}} nascosti)",
"fill": {
"grid": "Griglia",
"crosshatch": "Tratteggio incrociato",
"fillColor": "Colore di riempimento",
"fillStyle": "Stile riempimento",
"solid": "Solido",
"vertical": "Verticale",
"horizontal": "Orizzontale",
"diagonal": "Diagonale"
},
"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": "Livello",
"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}})",
"convertToControlLayer": "Converti in livello di controllo",
"convertToRasterLayer": "Converti in livello raster",
"unlocked": "Sbloccato",
"enableTransparencyEffect": "Abilita l'effetto trasparenza",
"replaceLayer": "Sostituisci livello",
"pullBboxIntoLayer": "Carica l'immagine delimitata nel riquadro",
"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": {
"isolatedFilteringPreview": "Anteprima del filtraggio isolata",
"isolatedStagingPreview": "Anteprima di generazione isolata",
"isolatedTransformingPreview": "Anteprima di trasformazione isolata",
"isolatedPreview": "Anteprima isolata",
"invertBrushSizeScrollDirection": "Inverti scorrimento per dimensione pennello",
"snapToGrid": {
"label": "Aggancia alla griglia",
"on": "Acceso",
"off": "Spento"
},
"pressureSensitivity": "Sensibilità alla pressione",
"preserveMask": {
"alert": "Preservare la regione mascherata",
"label": "Preserva la regione mascherata"
}
},
"transform": {
"reset": "Reimposta",
"fitToBbox": "Adatta al Riquadro",
"transform": "Trasforma",
"apply": "Applica",
"cancel": "Annulla"
},
"stagingArea": {
"next": "Successiva",
"discard": "Scarta",
"discardAll": "Scarta tutto",
"accept": "Accetta",
"saveToGallery": "Salva nella Galleria",
"previous": "Precedente",
"showResultsOn": "Risultati visualizzati",
"showResultsOff": "Risultati nascosti"
},
"HUD": {
"bbox": "Riquadro di delimitazione",
"entityStatus": {
"isHidden": "{{title}} è nascosto",
"isLocked": "{{title}} è bloccato",
"isTransforming": "{{title}} sta trasformando",
"isFiltering": "{{title}} sta filtrando",
"isEmpty": "{{title}} è vuoto",
"isDisabled": "{{title}} è disabilitato"
},
"scaledBbox": "Riquadro scalato"
},
"canvasContextMenu": {
"newControlLayer": "Nuovo Livello di Controllo",
"newRegionalReferenceImage": "Nuova immagine di riferimento Regionale",
"newGlobalReferenceImage": "Nuova immagine di riferimento Globale",
"bboxGroup": "Crea dal riquadro di delimitazione",
"saveBboxToGallery": "Salva il riquadro nella Galleria",
"cropCanvasToBbox": "Ritaglia la Tela al riquadro",
"canvasGroup": "Tela",
"newRasterLayer": "Nuovo Livello Raster",
"saveCanvasToGallery": "Salva la Tela nella Galleria",
"saveToGalleryGroup": "Salva nella Galleria"
}
},
"ui": {
"tabs": {
@@ -1545,7 +1906,8 @@
"modelsTab": "$t(ui.tabs.models) $t(common.tab)",
"queue": "Coda",
"upscaling": "Amplia",
"upscalingTab": "$t(ui.tabs.upscaling) $t(common.tab)"
"upscalingTab": "$t(ui.tabs.upscaling) $t(common.tab)",
"gallery": "Galleria"
}
},
"upscaling": {
@@ -1615,5 +1977,45 @@
"noTemplates": "Nessun modello",
"acceptedColumnsKeys": "Colonne/chiavi accettate:",
"promptTemplateCleared": "Modello di prompt cancellato"
},
"newUserExperience": {
"gettingStartedSeries": "Desideri maggiori informazioni? Consulta la nostra <LinkComponent>Getting Started Series</LinkComponent> per suggerimenti su come sfruttare appieno il potenziale di Invoke Studio.",
"toGetStarted": "Per iniziare, inserisci un prompt nella casella e fai clic su <StrongComponent>Invoke</StrongComponent> per generare la tua prima immagine. Puoi scegliere di salvare le tue immagini direttamente nella <StrongComponent>Galleria</StrongComponent> o modificarle nella <StrongComponent>Tela</StrongComponent>."
},
"whatsNew": {
"canvasV2Announcement": {
"readReleaseNotes": "Leggi le Note di Rilascio",
"fluxSupport": "Supporto per la famiglia di modelli Flux",
"newCanvas": "Una nuova potente tela di controllo",
"watchReleaseVideo": "Guarda il video di rilascio",
"watchUiUpdatesOverview": "Guarda le novità dell'interfaccia",
"newLayerTypes": "Nuovi tipi di livello per un miglior controllo"
},
"whatsNewInInvoke": "Novità in Invoke"
},
"system": {
"logLevel": {
"info": "Info",
"warn": "Avviso",
"fatal": "Fatale",
"error": "Errore",
"debug": "Debug",
"trace": "Traccia",
"logLevel": "Livello di registro"
},
"logNamespaces": {
"workflows": "Flussi di lavoro",
"generation": "Generazione",
"canvas": "Tela",
"config": "Configurazione",
"models": "Modelli",
"gallery": "Galleria",
"queue": "Coda",
"events": "Eventi",
"system": "Sistema",
"metadata": "Metadati",
"logNamespaces": "Elementi del registro"
},
"enableLogging": "Abilita la registrazione"
}
}

View File

@@ -93,7 +93,8 @@
"placeholderSelectAModel": "Выбрать модель",
"reset": "Сброс",
"none": "Ничего",
"new": "Новый"
"new": "Новый",
"ok": "Ok"
},
"gallery": {
"galleryImageSize": "Размер изображений",
@@ -227,6 +228,118 @@
"selectBrushTool": {
"title": "Инструмент кисть",
"desc": "Выбирает кисть."
},
"selectBboxTool": {
"title": "Инструмент рамка",
"desc": "Выбрать инструмент «Ограничительная рамка»."
},
"incrementToolWidth": {
"desc": "Increment the brush or eraser tool width, whichever is selected.",
"title": "Increment Tool Width"
},
"selectColorPickerTool": {
"title": "Color Picker Tool",
"desc": "Select the color picker tool."
},
"prevEntity": {
"title": "Prev Layer",
"desc": "Select the previous layer in the list."
},
"filterSelected": {
"title": "Filter",
"desc": "Filter the selected layer. Only applies to Raster and Control layers."
},
"undo": {
"desc": "Отменяет последнее действие на холсте.",
"title": "Отменить"
},
"transformSelected": {
"title": "Transform",
"desc": "Transform the selected layer."
},
"setZoomTo400Percent": {
"title": "Zoom to 400%",
"desc": "Set the canvas zoom to 400%."
},
"setZoomTo200Percent": {
"title": "Zoom to 200%",
"desc": "Set the canvas zoom to 200%."
},
"deleteSelected": {
"desc": "Delete the selected layer.",
"title": "Delete Layer"
},
"resetSelected": {
"title": "Reset Layer",
"desc": "Reset the selected layer. Only applies to Inpaint Mask and Regional Guidance."
},
"redo": {
"desc": "Возвращает последнее отмененное действие.",
"title": "Вернуть"
},
"nextEntity": {
"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."
},
"cancelFilter": {
"title": "Cancel Filter",
"desc": "Cancel the pending filter."
},
"applyTransform": {
"desc": "Apply the pending transform to the selected layer.",
"title": "Apply Transform"
},
"cancelTransform": {
"title": "Cancel Transform",
"desc": "Cancel the pending transform."
},
"selectEraserTool": {
"title": "Eraser Tool",
"desc": "Select the eraser tool."
},
"fitLayersToCanvas": {
"desc": "Scale and position the view to fit all visible layers.",
"title": "Fit Layers to Canvas"
},
"decrementToolWidth": {
"title": "Decrement Tool Width",
"desc": "Decrement the brush or eraser tool width, whichever is selected."
},
"setZoomTo800Percent": {
"title": "Zoom to 800%",
"desc": "Set the canvas zoom to 800%."
},
"quickSwitch": {
"title": "Layer Quick Switch",
"desc": "Switch between the last two selected layers. If a layer is bookmarked, always switch between it and the last non-bookmarked layer."
},
"fitBboxToCanvas": {
"title": "Fit Bbox to Canvas",
"desc": "Scale and position the view to fit the bbox."
},
"setZoomTo100Percent": {
"title": "Zoom to 100%",
"desc": "Set the canvas zoom to 100%."
},
"selectMoveTool": {
"desc": "Select the move tool.",
"title": "Move Tool"
},
"selectRectTool": {
"title": "Rect Tool",
"desc": "Select the rect tool."
},
"selectViewTool": {
"title": "View Tool",
"desc": "Select the view tool."
}
},
"hotkeys": "Горячие клавиши",
@@ -236,11 +349,33 @@
"desc": "Отменить последнее действие в рабочем процессе."
},
"deleteSelection": {
"desc": "Удалить выделенные узлы и ребра."
"desc": "Удалить выделенные узлы и ребра.",
"title": "Delete"
},
"redo": {
"title": "Вернуть",
"desc": "Вернуть последнее действие в рабочем процессе."
},
"copySelection": {
"title": "Copy",
"desc": "Copy selected nodes and edges."
},
"pasteSelection": {
"title": "Paste",
"desc": "Paste copied nodes and edges."
},
"addNode": {
"desc": "Open the add node menu.",
"title": "Add Node"
},
"title": "Workflows",
"pasteSelectionWithEdges": {
"title": "Paste with Edges",
"desc": "Paste copied nodes, edges, and all edges connected to copied nodes."
},
"selectAll": {
"desc": "Select all nodes and edges.",
"title": "Select All"
}
},
"viewer": {
@@ -257,12 +392,84 @@
"title": "Восстановить все метаданные"
},
"swapImages": {
"desc": "Поменять местами сравниваемые изображения."
"desc": "Поменять местами сравниваемые изображения.",
"title": "Swap Comparison Images"
},
"title": "Просмотрщик изображений",
"toggleViewer": {
"title": "Открыть/закрыть просмотрщик",
"desc": "Показать или скрыть просмотрщик изображений. Доступно только на вкладке «Холст»."
},
"recallSeed": {
"title": "Recall Seed",
"desc": "Recall the seed for the current image."
},
"recallPrompts": {
"desc": "Recall the positive and negative prompts for the current image.",
"title": "Recall Prompts"
},
"remix": {
"title": "Remix",
"desc": "Recall all metadata except for the seed for the current image."
},
"useSize": {
"desc": "Use the current image's size as the bbox size.",
"title": "Use Size"
},
"runPostprocessing": {
"title": "Run Postprocessing",
"desc": "Run the selected postprocessing on the current image."
},
"toggleMetadata": {
"title": "Show/Hide Metadata",
"desc": "Show or hide the current image's metadata overlay."
}
},
"gallery": {
"galleryNavRightAlt": {
"desc": "Same as Navigate Right, but selects the compare image, opening compare mode if it isn't already open.",
"title": "Navigate Right (Compare Image)"
},
"galleryNavRight": {
"desc": "Navigate right in the gallery grid, selecting that image. If at the last image of the row, go to the next row. If at the last image of the page, go to the next page.",
"title": "Navigate Right"
},
"galleryNavUp": {
"desc": "Navigate up in the gallery grid, selecting that image. If at the top of the page, go to the previous page.",
"title": "Navigate Up"
},
"galleryNavDown": {
"title": "Navigate Down",
"desc": "Navigate down in the gallery grid, selecting that image. If at the bottom of the page, go to the next page."
},
"galleryNavLeft": {
"title": "Navigate Left",
"desc": "Navigate left in the gallery grid, selecting that image. If at the first image of the row, go to the previous row. If at the first image of the page, go to the previous page."
},
"galleryNavDownAlt": {
"title": "Navigate Down (Compare Image)",
"desc": "Same as Navigate Down, but selects the compare image, opening compare mode if it isn't already open."
},
"galleryNavLeftAlt": {
"desc": "Same as Navigate Left, but selects the compare image, opening compare mode if it isn't already open.",
"title": "Navigate Left (Compare Image)"
},
"clearSelection": {
"desc": "Clear the current selection, if any.",
"title": "Clear Selection"
},
"deleteSelection": {
"title": "Delete",
"desc": "Delete all selected images. By default, you will be prompted to confirm deletion. If the images are currently in use in the app, you will be warned."
},
"galleryNavUpAlt": {
"title": "Navigate Up (Compare Image)",
"desc": "Same as Navigate Up, but selects the compare image, opening compare mode if it isn't already open."
},
"title": "Gallery",
"selectAllOnPage": {
"title": "Select All On Page",
"desc": "Select all images on the current page."
}
}
},
@@ -372,7 +579,9 @@
"ipAdapters": "IP адаптеры",
"starterModelsInModelManager": "Стартовые модели можно найти в Менеджере моделей",
"learnMoreAboutSupportedModels": "Подробнее о поддерживаемых моделях",
"t5Encoder": "T5 энкодер"
"t5Encoder": "T5 энкодер",
"spandrelImageToImage": "Image to Image (Spandrel)",
"clipEmbed": "CLIP Embed"
},
"parameters": {
"images": "Изображения",
@@ -432,12 +641,16 @@
"rgNoRegion": "регион не выбран",
"rgNoPromptsOrIPAdapters": "нет текстовых запросов или IP-адаптеров",
"ipAdapterIncompatibleBaseModel": "несовместимая базовая модель IP-адаптера",
"ipAdapterNoImageSelected": "изображение IP-адаптера не выбрано"
"ipAdapterNoImageSelected": "изображение IP-адаптера не выбрано",
"t2iAdapterIncompatibleScaledBboxWidth": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, масштабированная ширина рамки {{width}}",
"t2iAdapterIncompatibleBboxHeight": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, высота рамки {{height}}",
"t2iAdapterIncompatibleBboxWidth": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, ширина рамки {{width}}",
"t2iAdapterIncompatibleScaledBboxHeight": "$t(parameters.invoke.layer.t2iAdapterRequiresDimensionsToBeMultipleOf) {{multiple}}, масштабированная высота рамки {{height}}"
},
"fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), ширина bbox {{width}}",
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), высота bbox {{height}}",
"fluxModelIncompatibleScaledBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), масштабированная высота bbox {{height}}",
"fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16) масштабированная ширина bbox {{width}}",
"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": "Холст фильтруется",
@@ -470,7 +683,8 @@
"staged": "Инсценировка",
"optimizedImageToImage": "Оптимизированное img2img",
"sendToCanvas": "Отправить на холст",
"guidance": "Точность"
"guidance": "Точность",
"boxBlur": "Box Blur"
},
"settings": {
"models": "Модели",
@@ -504,7 +718,8 @@
"intermediatesClearedFailed": "Проблема очистки промежуточных",
"reloadingIn": "Перезагрузка через",
"informationalPopoversDisabled": "Информационные всплывающие окна отключены",
"informationalPopoversDisabledDesc": "Информационные всплывающие окна были отключены. Включите их в Настройках."
"informationalPopoversDisabledDesc": "Информационные всплывающие окна были отключены. Включите их в Настройках.",
"confirmOnNewSession": "Подтверждение нового сеанса"
},
"toast": {
"uploadFailed": "Загрузка не удалась",
@@ -518,8 +733,8 @@
"parameterSet": "Параметр задан",
"problemCopyingImage": "Не удается скопировать изображение",
"baseModelChangedCleared_one": "Очищена или отключена {{count}} несовместимая подмодель",
"baseModelChangedCleared_few": "Очищены или отключены {{count}} несовместимые подмодели",
"baseModelChangedCleared_many": "Очищены или отключены {{count}} несовместимых подмоделей",
"baseModelChangedCleared_few": "Очищено или отключено {{count}} несовместимых подмодели",
"baseModelChangedCleared_many": "Очищено или отключено {{count}} несовместимых подмоделей",
"loadedWithWarnings": "Рабочий процесс загружен с предупреждениями",
"setControlImage": "Установить как контрольное изображение",
"setNodeField": "Установить как поле узла",
@@ -573,7 +788,9 @@
"resetUI": "$t(accessibility.reset) интерфейс",
"createIssue": "Сообщить о проблеме",
"about": "Об этом",
"submitSupportTicket": "Отправить тикет в службу поддержки"
"submitSupportTicket": "Отправить тикет в службу поддержки",
"toggleRightPanel": "Переключить правую панель (G)",
"toggleLeftPanel": "Переключить левую панель (T)"
},
"nodes": {
"zoomInNodes": "Увеличьте масштаб",
@@ -730,16 +947,16 @@
"loading": "Загрузка...",
"clearSearch": "Очистить поиск",
"deleteBoardOnly": "Удалить только доску",
"movingImagesToBoard_one": "Перемещаем {{count}} изображение на доску:",
"movingImagesToBoard_few": "Перемещаем {{count}} изображения на доску:",
"movingImagesToBoard_many": "Перемещаем {{count}} изображений на доску:",
"movingImagesToBoard_one": "Перемещение {{count}} изображения на доску:",
"movingImagesToBoard_few": "Перемещение {{count}} изображений на доску:",
"movingImagesToBoard_many": "Перемещение {{count}} изображений на доску:",
"downloadBoard": "Скачать доску",
"deleteBoard": "Удалить доску",
"deleteBoardAndImages": "Удалить доску и изображения",
"deletedBoardsCannotbeRestored": "Удаленные доски не могут быть восстановлены. Выбор «Удалить только доску» переведет изображения в состояние без категории.",
"assetsWithCount_one": "{{count}} ассет",
"assetsWithCount_few": "{{count}} ассета",
"assetsWithCount_many": "{{count}} ассетов",
"assetsWithCount_one": "{{count}} актив",
"assetsWithCount_few": "{{count}} актива",
"assetsWithCount_many": "{{count}} активов",
"imagesWithCount_one": "{{count}} изображение",
"imagesWithCount_few": "{{count}} изображения",
"imagesWithCount_many": "{{count}} изображений",
@@ -755,7 +972,8 @@
"hideBoards": "Скрыть доски",
"viewBoards": "Просмотреть доски",
"noBoards": "Нет досок {{boardType}}",
"deletedPrivateBoardsCannotbeRestored": "Удаленные доски не могут быть восстановлены. Выбор «Удалить только доску» переведет изображения в приватное состояние без категории для создателя изображения."
"deletedPrivateBoardsCannotbeRestored": "Удаленные доски не могут быть восстановлены. Выбор «Удалить только доску» переведет изображения в приватное состояние без категории для создателя изображения.",
"updateBoardError": "Ошибка обновления доски"
},
"dynamicPrompts": {
"seedBehaviour": {
@@ -1392,15 +1610,15 @@
"autoNegative": "Авто негатив",
"deletePrompt": "Удалить запрос",
"rectangle": "Прямоугольник",
"addNegativePrompt": "Добавить $t(common.negativePrompt)",
"addNegativePrompt": "Добавить $t(controlLayers.negativePrompt)",
"regionalGuidance": "Региональная точность",
"opacity": "Непрозрачность",
"addLayer": "Добавить слой",
"moveToFront": "На передний план",
"addPositivePrompt": "Добавить $t(common.positivePrompt)",
"addPositivePrompt": "Добавить $t(controlLayers.prompt)",
"regional": "Региональный",
"bookmark": "Закладка для быстрого переключения",
"fitBboxToLayers": "Подогнать Bbox к слоям",
"fitBboxToLayers": "Подогнать рамку к слоям",
"mergeVisibleOk": "Объединенные видимые слои",
"mergeVisibleError": "Ошибка объединения видимых слоев",
"clearHistory": "Очистить историю",
@@ -1409,7 +1627,7 @@
"saveLayerToAssets": "Сохранить слой в активы",
"clearCaches": "Очистить кэши",
"recalculateRects": "Пересчитать прямоугольники",
"saveBboxToGallery": "Сохранить Bbox в галерею",
"saveBboxToGallery": "Сохранить рамку в галерею",
"resetCanvas": "Сбросить холст",
"canvas": "Холст",
"global": "Глобальный",
@@ -1421,15 +1639,278 @@
"newRasterLayerOk": "Создан растровый слой",
"newRasterLayerError": "Ошибка создания растрового слоя",
"newGlobalReferenceImageOk": "Создано глобальное эталонное изображение",
"bboxOverlay": "Показать наложение Bbox",
"bboxOverlay": "Показать наложение ограничительной рамки",
"saveCanvasToGallery": "Сохранить холст в галерею",
"pullBboxIntoReferenceImageOk": "Bbox перенесен в эталонное изображение",
"pullBboxIntoReferenceImageError": "Ошибка переноса BBox в эталонное изображение",
"pullBboxIntoReferenceImageOk": "рамка перенесена в эталонное изображение",
"pullBboxIntoReferenceImageError": "Ошибка переноса рамки в эталонное изображение",
"regionIsEmpty": "Выбранный регион пуст",
"savedToGalleryOk": "Сохранено в галерею",
"savedToGalleryError": "Ошибка сохранения в галерею",
"pullBboxIntoLayerOk": "Bbox перенесен в слой",
"pullBboxIntoLayerError": "Проблема с переносом BBox в слой"
"pullBboxIntoLayerOk": "Рамка перенесена в слой",
"pullBboxIntoLayerError": "Проблема с переносом рамки в слой",
"newLayerFromImage": "Новый слой из изображения",
"filter": {
"lineart_anime_edge_detection": {
"label": "Обнаружение краев Lineart Anime",
"description": "Создает карту краев выбранного слоя с помощью модели обнаружения краев Lineart Anime."
},
"hed_edge_detection": {
"scribble": "Штрих",
"label": "обнаружение границ HED",
"description": "Создает карту границ из выбранного слоя с использованием модели обнаружения границ HED."
},
"mlsd_detection": {
"description": "Генерирует карту сегментов линий из выбранного слоя с помощью модели обнаружения сегментов линий MLSD.",
"score_threshold": "Пороговый балл",
"distance_threshold": "Порог расстояния",
"label": "Обнаружение сегментов линии"
},
"canny_edge_detection": {
"low_threshold": "Низкий порог",
"high_threshold": "Высокий порог",
"label": "Обнаружение краев",
"description": "Создает карту краев выбранного слоя с помощью алгоритма обнаружения краев Canny."
},
"color_map": {
"description": "Создайте цветовую карту из выбранного слоя.",
"label": "Цветная карта",
"tile_size": "Размер плитки"
},
"depth_anything_depth_estimation": {
"model_size_base": "Базовая",
"model_size_large": "Большая",
"label": "Анализ глубины",
"model_size_small": "Маленькая",
"model_size_small_v2": "Маленькая v2",
"description": "Создает карту глубины из выбранного слоя с использованием модели Depth Anything.",
"model_size": "Размер модели"
},
"mediapipe_face_detection": {
"min_confidence": "Минимальная уверенность",
"label": "Распознавание лиц MediaPipe",
"description": "Обнаруживает лица в выбранном слое с помощью модели обнаружения лиц MediaPipe.",
"max_faces": "Максимум лиц"
},
"lineart_edge_detection": {
"label": "Обнаружение краев Lineart",
"description": "Создает карту краев выбранного слоя с помощью модели обнаружения краев Lineart.",
"coarse": "Грубый"
},
"filterType": "Тип фильтра",
"autoProcess": "Автообработка",
"reset": "Сбросить",
"content_shuffle": {
"scale_factor": "Коэффициент",
"label": "Перетасовка контента",
"description": "Перемешивает содержимое выбранного слоя, аналогично эффекту «сжижения»."
},
"dw_openpose_detection": {
"label": "Обнаружение DW Openpose",
"draw_hands": "Рисовать руки",
"description": "Обнаруживает позы человека в выбранном слое с помощью модели DW Openpose.",
"draw_face": "Рисовать лицо",
"draw_body": "Рисовать тело"
},
"normal_map": {
"label": "Карта нормалей",
"description": "Создает карту нормалей для выбранного слоя."
},
"spandrel_filter": {
"model": "Модель",
"label": "Модель img2img",
"autoScale": "Авто масштабирование",
"scale": "Целевой масштаб",
"description": "Запустить модель изображения к изображению на выбранном слое.",
"autoScaleDesc": "Выбранная модель будет работать до тех пор, пока не будет достигнут целевой масштаб."
},
"pidi_edge_detection": {
"scribble": "Штрих",
"description": "Генерирует карту краев из выбранного слоя с помощью модели обнаружения краев PiDiNet.",
"label": "Обнаружение краев PiDiNet",
"quantize_edges": "Квантизация краев"
},
"process": "Обработать",
"apply": "Применить",
"cancel": "Отменить",
"filter": "Фильтр",
"filters": "Фильтры"
},
"HUD": {
"entityStatus": {
"isHidden": "{{title}} скрыт",
"isLocked": "{{title}} заблокирован",
"isDisabled": "{{title}} отключен",
"isEmpty": "{{title}} пуст",
"isFiltering": "{{title}} фильтруется",
"isTransforming": "{{title}} трансформируется"
},
"scaledBbox": "Масштабированная рамка",
"bbox": "Ограничительная рамка"
},
"canvasContextMenu": {
"saveBboxToGallery": "Сохранить рамку в галерею",
"newGlobalReferenceImage": "Новое глобальное эталонное изображение",
"bboxGroup": "Сохдать из рамки",
"canvasGroup": "Холст",
"newControlLayer": "Новый контрольный слой",
"newRasterLayer": "Новый растровый слой",
"saveToGalleryGroup": "Сохранить в галерею",
"saveCanvasToGallery": "Сохранить холст в галерею",
"cropCanvasToBbox": "Обрезать холст по рамке",
"newRegionalReferenceImage": "Новое региональное эталонное изображение"
},
"fill": {
"solid": "Сплошной",
"fillStyle": "Стиль заполнения",
"fillColor": "Цвет заполнения",
"grid": "Сетка",
"horizontal": "Горизонтальная",
"diagonal": "Диагональная",
"crosshatch": "Штриховка",
"vertical": "Вертикальная"
},
"showHUD": "Показать HUD",
"copyToClipboard": "Копировать в буфер обмена",
"ipAdapterMethod": {
"composition": "Только композиция",
"style": "Только стиль",
"ipAdapterMethod": "Метод IP адаптера",
"full": "Полный"
},
"addReferenceImage": "Добавить $t(controlLayers.referenceImage)",
"inpaintMask": "Маска перерисовки",
"sendToGalleryDesc": "При нажатии кнопки Invoke создается изображение и сохраняется в вашей галерее.",
"sendToCanvas": "Отправить на холст",
"regionalGuidance_withCount_one": "$t(controlLayers.regionalGuidance)",
"regionalGuidance_withCount_few": "Региональных точности",
"regionalGuidance_withCount_many": "Региональных точностей",
"controlLayer_withCount_one": "$t(controlLayers.controlLayer)",
"controlLayer_withCount_few": "Контрольных слоя",
"controlLayer_withCount_many": "Контрольных слоев",
"newCanvasFromImage": "Новый холст из изображения",
"inpaintMask_withCount_one": "$t(controlLayers.inpaintMask)",
"inpaintMask_withCount_few": "Маски перерисовки",
"inpaintMask_withCount_many": "Масок перерисовки",
"globalReferenceImages_withCount_visible": "Глобальные эталонные изображения ({{count}})",
"controlMode": {
"prompt": "Запрос",
"controlMode": "Режим контроля",
"megaControl": "Мега контроль",
"balanced": "Сбалансированный",
"control": "Контроль"
},
"settings": {
"isolatedPreview": "Изолированный предпросмотр",
"isolatedTransformingPreview": "Изолированный предпросмотр преобразования",
"invertBrushSizeScrollDirection": "Инвертировать прокрутку для размера кисти",
"snapToGrid": {
"label": "Привязка к сетке",
"on": "Вкл",
"off": "Выкл"
},
"isolatedFilteringPreview": "Изолированный предпросмотр фильтрации",
"pressureSensitivity": "Чувствительность к давлению",
"isolatedStagingPreview": "Изолированный предпросмотр на промежуточной стадии",
"preserveMask": {
"label": "Сохранить замаскированную область",
"alert": "Сохранение замаскированной области"
}
},
"stagingArea": {
"discardAll": "Отбросить все",
"discard": "Отбросить",
"accept": "Принять",
"previous": "Предыдущий",
"next": "Следующий",
"saveToGallery": "Сохранить в галерею",
"showResultsOn": "Показать результаты",
"showResultsOff": "Скрыть результаты"
},
"pullBboxIntoReferenceImage": "Поместить рамку в эталонное изображение",
"enableAutoNegative": "Включить авто негатив",
"maskFill": "Заполнение маски",
"viewProgressInViewer": "Просматривайте прогресс и результаты в <Btn>Просмотрщике изображений</Btn>.",
"convertToRasterLayer": "Конвертировать в растровый слой",
"tool": {
"move": "Двигать",
"bbox": "Ограничительная рамка",
"view": "Смотреть",
"brush": "Кисть",
"eraser": "Ластик",
"rectangle": "Прямоугольник",
"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": "Заменить слой",
"width": "Ширина",
"controlLayer": "Слой управления",
"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": "Слой",
"prompt": "Запрос",
"negativePrompt": "Исключающий запрос",
"beginEndStepPercentShort": "Начало/конец %",
"transform": {
"transform": "Трансформировать",
"fitToBbox": "Вместить в рамку",
"reset": "Сбросить",
"apply": "Применить",
"cancel": "Отменить"
},
"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}} скрыто)",
"convertToControlLayer": "Конвертировать в контрольный слой",
"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)"
},
"ui": {
"tabs": {
@@ -1441,7 +1922,8 @@
"modelsTab": "$t(ui.tabs.models) $t(common.tab)",
"queue": "Очередь",
"upscaling": "Увеличение",
"upscalingTab": "$t(ui.tabs.upscaling) $t(common.tab)"
"upscalingTab": "$t(ui.tabs.upscaling) $t(common.tab)",
"gallery": "Галерея"
}
},
"upscaling": {
@@ -1513,5 +1995,45 @@
"professional": "Профессионал",
"professionalUpsell": "Доступно в профессиональной версии Invoke. Нажмите здесь или посетите invoke.com/pricing для получения более подробной информации.",
"shareAccess": "Поделиться доступом"
},
"system": {
"logNamespaces": {
"canvas": "Холст",
"config": "Конфигурация",
"generation": "Генерация",
"workflows": "Рабочие процессы",
"gallery": "Галерея",
"models": "Модели",
"logNamespaces": "Пространства имен логов",
"events": "События",
"system": "Система",
"queue": "Очередь",
"metadata": "Метаданные"
},
"enableLogging": "Включить логи",
"logLevel": {
"logLevel": "Уровень логов",
"fatal": "Фатальное",
"debug": "Отладка",
"info": "Инфо",
"warn": "Предупреждение",
"error": "Ошибки",
"trace": "Трассировка"
}
},
"whatsNew": {
"canvasV2Announcement": {
"newLayerTypes": "Новые типы слоев для еще большего контроля",
"readReleaseNotes": "Прочитать информацию о выпуске",
"watchReleaseVideo": "Смотреть видео о выпуске",
"fluxSupport": "Поддержка семейства моделей Flux",
"newCanvas": "Новый мощный холст управления",
"watchUiUpdatesOverview": "Обзор обновлений пользовательского интерфейса"
},
"whatsNewInInvoke": "Что нового в Invoke"
},
"newUserExperience": {
"toGetStarted": "Чтобы начать работу, введите в поле запрос и нажмите <StrongComponent>Invoke</StrongComponent>, чтобы сгенерировать первое изображение. Вы можете сохранить изображения непосредственно в <StrongComponent>Галерею</StrongComponent> или отредактировать их на <StrongComponent>Холсте</StrongComponent>.",
"gettingStartedSeries": "Хотите получить больше рекомендаций? Ознакомьтесь с нашей серией <LinkComponent>Getting Started Series</LinkComponent> для получения советов по раскрытию всего потенциала Invoke Studio."
}
}

View File

@@ -415,7 +415,8 @@
"resetUI": "$t(accessibility.reset) UI",
"createIssue": "创建问题",
"about": "关于",
"submitSupportTicket": "提交支持工单"
"submitSupportTicket": "提交支持工单",
"toggleRightPanel": "切换右侧面板(G)"
},
"nodes": {
"zoomInNodes": "放大",

View File

@@ -88,8 +88,8 @@ const App = ({ config = DEFAULT_CONFIG, studioInitAction }: Props) => {
<ErrorBoundary onReset={handleReset} FallbackComponent={AppErrorBoundaryFallback}>
<Box
id="invoke-app-wrapper"
w="100vw"
h="100vh"
w="100dvw"
h="100dvh"
position="relative"
overflow="hidden"
{...dropzone.getRootProps()}

View File

@@ -44,7 +44,7 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
}, [error.message, error.name, isLocal]);
return (
<Flex layerStyle="body" w="100vw" h="100vh" alignItems="center" justifyContent="center" p={4}>
<Flex layerStyle="body" w="100dvw" h="100dvh" alignItems="center" justifyContent="center" p={4}>
<Flex layerStyle="first" flexDir="column" borderRadius="base" justifyContent="center" gap={8} p={16}>
<Flex alignItems="center" gap="2">
<Image src={InvokeLogoYellow} alt="invoke-logo" w="24px" h="24px" minW="24px" minH="24px" userSelect="none" />

View File

@@ -1,80 +1,62 @@
import { Flex, Text } from '@invoke-ai/ui-library';
import type { AnimationProps } from 'framer-motion';
import { motion } from 'framer-motion';
import { memo, useRef } from 'react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { v4 as uuidv4 } from 'uuid';
type Props = {
isOver: boolean;
label?: string;
};
const initial: AnimationProps['initial'] = {
opacity: 0,
};
const animate: AnimationProps['animate'] = {
opacity: 1,
transition: { duration: 0.1 },
};
const exit: AnimationProps['exit'] = {
opacity: 0,
transition: { duration: 0.1 },
};
const IAIDropOverlay = (props: Props) => {
const { t } = useTranslation();
const { isOver, label = t('gallery.drop') } = props;
const motionId = useRef(uuidv4());
return (
<motion.div key={motionId.current} initial={initial} animate={animate} exit={exit}>
<Flex position="absolute" top={0} right={0} bottom={0} left={0}>
<Flex
position="absolute"
top={0}
right={0}
bottom={0}
left={0}
w="full"
h="full"
bg="base.900"
opacity={0.7}
borderRadius="base"
alignItems="center"
justifyContent="center"
transitionProperty="common"
transitionDuration="0.1s"
/>
<Flex position="absolute" top={0} right={0} bottom={0} left={0}>
<Flex
position="absolute"
top={0}
right={0}
bottom={0}
left={0}
w="full"
h="full"
bg="base.900"
opacity={0.7}
borderRadius="base"
alignItems="center"
justifyContent="center"
transitionProperty="common"
transitionDuration="0.1s"
/>
<Flex
position="absolute"
top={0.5}
right={0.5}
bottom={0.5}
left={0.5}
opacity={1}
borderWidth={1.5}
borderColor={isOver ? 'invokeYellow.300' : 'base.500'}
borderRadius="base"
borderStyle="dashed"
<Flex
position="absolute"
top={0.5}
right={0.5}
bottom={0.5}
left={0.5}
opacity={1}
borderWidth={1.5}
borderColor={isOver ? 'invokeYellow.300' : 'base.500'}
borderRadius="base"
borderStyle="dashed"
transitionProperty="common"
transitionDuration="0.1s"
alignItems="center"
justifyContent="center"
>
<Text
fontSize="lg"
fontWeight="semibold"
color={isOver ? 'invokeYellow.300' : 'base.500'}
transitionProperty="common"
transitionDuration="0.1s"
alignItems="center"
justifyContent="center"
p={4}
textAlign="center"
>
<Text
fontSize="lg"
fontWeight="semibold"
color={isOver ? 'invokeYellow.300' : 'base.500'}
transitionProperty="common"
transitionDuration="0.1s"
textAlign="center"
>
{label}
</Text>
</Flex>
{label}
</Text>
</Flex>
</motion.div>
</Flex>
);
};

View File

@@ -45,8 +45,8 @@ const ImageUploadOverlay = (props: ImageUploadOverlayProps) => {
position="absolute"
top={0}
insetInlineStart={0}
width="100vw"
height="100vh"
width="100dvw"
height="100dvh"
zIndex={999}
backdropFilter="blur(20px)"
>

View File

@@ -6,7 +6,7 @@ import { memo } from 'react';
const Loading = () => {
return (
<Flex position="relative" width="100vw" height="100vh" alignItems="center" justifyContent="center" bg="#151519">
<Flex position="relative" width="100dvw" height="100dvh" alignItems="center" justifyContent="center" bg="#151519">
<Image src={InvokeLogoWhite} w="8rem" h="8rem" />
<Spinner
label="Loading"

View File

@@ -8,6 +8,7 @@ import type {
} from 'features/dnd/types';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
const addRasterLayerFromImageDropData: AddRasterLayerFromImageDropData = {
id: 'add-raster-layer-from-image-drop-data',
@@ -30,6 +31,7 @@ const addGlobalReferenceImageFromImageDropData: AddGlobalReferenceImageFromImage
};
export const CanvasDropArea = memo(() => {
const { t } = useTranslation();
const imageViewer = useImageViewer();
if (imageViewer.isOpen) {
@@ -49,16 +51,28 @@ export const CanvasDropArea = memo(() => {
pointerEvents="none"
>
<GridItem position="relative">
<IAIDroppable dropLabel="New Raster Layer" data={addRasterLayerFromImageDropData} />
<IAIDroppable
dropLabel={t('controlLayers.canvasContextMenu.newRasterLayer')}
data={addRasterLayerFromImageDropData}
/>
</GridItem>
<GridItem position="relative">
<IAIDroppable dropLabel="New Control Layer" data={addControlLayerFromImageDropData} />
<IAIDroppable
dropLabel={t('controlLayers.canvasContextMenu.newControlLayer')}
data={addControlLayerFromImageDropData}
/>
</GridItem>
<GridItem position="relative">
<IAIDroppable dropLabel="New Regional Reference Image" data={addRegionalReferenceImageFromImageDropData} />
<IAIDroppable
dropLabel={t('controlLayers.canvasContextMenu.newRegionalReferenceImage')}
data={addRegionalReferenceImageFromImageDropData}
/>
</GridItem>
<GridItem position="relative">
<IAIDroppable dropLabel="New Global Reference Image" data={addGlobalReferenceImageFromImageDropData} />
<IAIDroppable
dropLabel={t('controlLayers.canvasContextMenu.newGlobalReferenceImage')}
data={addGlobalReferenceImageFromImageDropData}
/>
</GridItem>
</Grid>
</>

View File

@@ -1,4 +1,4 @@
import { ContextMenu, Flex, MenuList } from '@invoke-ai/ui-library';
import { ContextMenu, Flex, IconButton, Menu, MenuButton, MenuList } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { useFocusRegion } from 'common/hooks/focus';
import { CanvasAlertsPreserveMask } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask';
@@ -18,6 +18,18 @@ import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/Canva
import { selectDynamicGrid, selectShowHUD } from 'features/controlLayers/store/canvasSettingsSlice';
import { GatedImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer';
import { memo, useCallback, useRef } from 'react';
import { PiDotsThreeOutlineVerticalFill } from 'react-icons/pi';
const MenuContent = () => {
return (
<CanvasManagerProviderGate>
<MenuList>
<CanvasContextMenuGlobalMenuItems />
<CanvasContextMenuSelectedEntityMenuItems />
</MenuList>
</CanvasManagerProviderGate>
);
};
export const CanvasMainPanelContent = memo(() => {
const ref = useRef<HTMLDivElement>(null);
@@ -25,14 +37,7 @@ export const CanvasMainPanelContent = memo(() => {
const showHUD = useAppSelector(selectShowHUD);
const renderMenu = useCallback(() => {
return (
<CanvasManagerProviderGate>
<MenuList>
<CanvasContextMenuGlobalMenuItems />
<CanvasContextMenuSelectedEntityMenuItems />
</MenuList>
</CanvasManagerProviderGate>
);
return <MenuContent />;
}, []);
useFocusRegion('canvas', ref);
@@ -53,7 +58,7 @@ export const CanvasMainPanelContent = memo(() => {
<CanvasManagerProviderGate>
<CanvasToolbar />
</CanvasManagerProviderGate>
<ContextMenu<HTMLDivElement> renderMenu={renderMenu}>
<ContextMenu<HTMLDivElement> renderMenu={renderMenu} withLongPress={false}>
{(ref) => (
<Flex
ref={ref}
@@ -75,6 +80,12 @@ export const CanvasMainPanelContent = memo(() => {
<CanvasAlertsPreserveMask />
<CanvasAlertsSendingToGallery />
</Flex>
<Flex position="absolute" top={1} insetInlineEnd={1}>
<Menu>
<MenuButton as={IconButton} icon={<PiDotsThreeOutlineVerticalFill />} colorScheme="base" />
<MenuContent />
</Menu>
</Flex>
</CanvasManagerProviderGate>
</Flex>
)}

View File

@@ -49,7 +49,16 @@ export const IPAdapterImagePreview = memo(({ image, onChangeImage, droppableData
}, [handleResetControlImage, isConnected, isErrorControlImage]);
return (
<Flex position="relative" w="full" h="full" alignItems="center">
<Flex
position="relative"
w="full"
h="full"
alignItems="center"
borderColor="error.500"
borderStyle="solid"
borderWidth={controlImage ? 0 : 1}
borderRadius="base"
>
<IAIDndImage
draggableData={draggableData}
droppableData={droppableData}

View File

@@ -26,10 +26,16 @@ export const RegionalGuidanceMenuItemsAddPromptsAndIPAdapter = memo(() => {
return (
<>
<MenuItem onClick={addRegionalGuidancePositivePrompt} isDisabled={!validActions.canAddPositivePrompt || isBusy}>
<MenuItem
onPointerUp={addRegionalGuidancePositivePrompt}
isDisabled={!validActions.canAddPositivePrompt || isBusy}
>
{t('controlLayers.addPositivePrompt')}
</MenuItem>
<MenuItem onClick={addRegionalGuidanceNegativePrompt} isDisabled={!validActions.canAddNegativePrompt || isBusy}>
<MenuItem
onPointerUp={addRegionalGuidanceNegativePrompt}
isDisabled={!validActions.canAddNegativePrompt || isBusy}
>
{t('controlLayers.addNegativePrompt')}
</MenuItem>
<MenuItem onClick={addRegionalGuidanceIPAdapter} isDisabled={isBusy}>

View File

@@ -24,7 +24,7 @@ export const CanvasEntityDeleteButton = memo(() => {
variant="link"
alignSelf="stretch"
icon={<PiTrashSimpleFill />}
onClick={onClick}
onPointerUp={onClick}
colorScheme="error"
isDisabled={isBusy}
/>

View File

@@ -96,6 +96,8 @@ export class CanvasStageModule extends CanvasModuleBase {
initialize = () => {
this.log.debug('Initializing module');
this.container.style.touchAction = 'none';
this.container.style.userSelect = 'none';
this.container.style.webkitUserSelect = 'none';
this.konva.stage.container(this.container);
this.setResizeObserver();
this.fitStageToContainer();

View File

@@ -124,7 +124,7 @@ export class CanvasToolBrush extends CanvasModuleBase {
const settings = this.manager.stateApi.getSettings();
const brushPreviewFill = this.manager.stateApi.getBrushPreviewColor();
const alignedCursorPos = alignCoordForTool(cursorPos, settings.brushWidth);
const alignedCursorPos = alignCoordForTool(cursorPos.relative, settings.brushWidth);
const radius = settings.brushWidth / 2;
// The circle is scaled
@@ -141,14 +141,14 @@ export class CanvasToolBrush extends CanvasModuleBase {
const twoPixels = this.manager.stage.unscale(2);
this.konva.innerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
x: cursorPos.relative.x,
y: cursorPos.relative.y,
innerRadius: radius,
outerRadius: radius + onePixel,
});
this.konva.outerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
x: cursorPos.relative.x,
y: cursorPos.relative.y,
innerRadius: radius + onePixel,
outerRadius: radius + twoPixels,
});

View File

@@ -207,6 +207,8 @@ export class CanvasToolColorPicker extends CanvasModuleBase {
this.setVisibility(true);
const { x, y } = cursorPos.relative;
const settings = this.manager.stateApi.getSettings();
const colorUnderCursor = this.parent.$colorUnderCursor.get();
const colorPickerInnerRadius = this.manager.stage.unscale(this.config.RING_INNER_RADIUS);
@@ -215,28 +217,28 @@ export class CanvasToolColorPicker extends CanvasModuleBase {
const twoPixels = this.manager.stage.unscale(2);
this.konva.ringCandidateColor.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
x,
y,
fill: rgbColorToString(colorUnderCursor),
innerRadius: colorPickerInnerRadius,
outerRadius: colorPickerOuterRadius,
});
this.konva.ringCurrentColor.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
x,
y,
fill: rgbColorToString(settings.color),
innerRadius: colorPickerInnerRadius,
outerRadius: colorPickerOuterRadius,
});
this.konva.ringInnerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
x,
y,
innerRadius: colorPickerOuterRadius,
outerRadius: colorPickerOuterRadius + onePixel,
});
this.konva.ringOuterBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
x,
y,
innerRadius: colorPickerOuterRadius + onePixel,
outerRadius: colorPickerOuterRadius + twoPixels,
});
@@ -249,35 +251,35 @@ export class CanvasToolColorPicker extends CanvasModuleBase {
);
this.konva.crosshairNorthOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x, cursorPos.y - size, cursorPos.x, cursorPos.y - space],
points: [x, y - size, x, y - space],
});
this.konva.crosshairNorthInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x, cursorPos.y - size, cursorPos.x, cursorPos.y - space],
points: [x, y - size, x, y - space],
});
this.konva.crosshairEastOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x + space, cursorPos.y, cursorPos.x + size, cursorPos.y],
points: [x + space, y, x + size, y],
});
this.konva.crosshairEastInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x + space, cursorPos.y, cursorPos.x + size, cursorPos.y],
points: [x + space, y, x + size, y],
});
this.konva.crosshairSouthOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x, cursorPos.y + space, cursorPos.x, cursorPos.y + size],
points: [x, y + space, x, y + size],
});
this.konva.crosshairSouthInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x, cursorPos.y + space, cursorPos.x, cursorPos.y + size],
points: [x, y + space, x, y + size],
});
this.konva.crosshairWestOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x - space, cursorPos.y, cursorPos.x - size, cursorPos.y],
points: [x - space, y, x - size, y],
});
this.konva.crosshairWestInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x - space, cursorPos.y, cursorPos.x - size, cursorPos.y],
points: [x - space, y, x - size, y],
});
};

View File

@@ -104,7 +104,7 @@ export class CanvasToolEraser extends CanvasModuleBase {
this.setVisibility(true);
const settings = this.manager.stateApi.getSettings();
const alignedCursorPos = alignCoordForTool(cursorPos, settings.eraserWidth);
const alignedCursorPos = alignCoordForTool(cursorPos.relative, settings.eraserWidth);
const radius = settings.eraserWidth / 2;
// The circle is scaled
@@ -119,14 +119,14 @@ export class CanvasToolEraser extends CanvasModuleBase {
const twoPixels = this.manager.stage.unscale(2);
this.konva.innerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
x: cursorPos.relative.x,
y: cursorPos.relative.y,
innerRadius: radius,
outerRadius: radius + onePixel,
});
this.konva.outerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
x: cursorPos.relative.x,
y: cursorPos.relative.y,
innerRadius: radius + onePixel,
outerRadius: radius + twoPixels,
});

View File

@@ -7,12 +7,12 @@ import {
alignCoordForTool,
calculateNewBrushSizeFromWheelDelta,
floorCoord,
getColorAtCoordinate,
getIsPrimaryMouseDown,
getLastPointOfLastLine,
getLastPointOfLastLineWithPressure,
getLastPointOfLine,
getPrefixedId,
getScaledCursorPosition,
isDistanceMoreThanMin,
offsetCoord,
} from 'features/controlLayers/konva/util';
@@ -31,6 +31,7 @@ import { RGBA_BLACK } from 'features/controlLayers/store/types';
import Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node';
import { atom } from 'nanostores';
import rafThrottle from 'raf-throttle';
import type { Logger } from 'roarr';
// Konva's docs say the default drag buttons are [0], but it's actually [0,1]. We only want left-click to drag, so we
@@ -38,6 +39,11 @@ import type { Logger } from 'roarr';
// TODO(psyche): Fix the docs upstream!
Konva.dragButtons = [0];
// Typo insurance
const KEY_ESCAPE = 'Escape';
const KEY_SPACE = ' ';
const KEY_ALT = 'Alt';
type CanvasToolModuleConfig = {
BRUSH_SPACING_TARGET_SCALE: number;
};
@@ -77,7 +83,7 @@ export class CanvasToolModule extends CanvasModuleBase {
/**
* The last cursor position.
*/
$cursorPos = atom<Coordinate | null>(null);
$cursorPos = atom<{ relative: Coordinate; absolute: Coordinate } | null>(null);
/**
* The color currently under the cursor. Only has a value when the color picker tool is active.
*/
@@ -206,32 +212,15 @@ export class CanvasToolModule extends CanvasModuleBase {
}
};
syncLastCursorPos = (): Coordinate | null => {
const pos = getScaledCursorPosition(this.konva.stage);
this.$cursorPos.set(pos);
return pos;
};
syncCursorPositions = () => {
const relative = this.konva.stage.getRelativePointerPosition();
const absolute = this.konva.stage.getPointerPosition();
getColorUnderCursor = (): RgbColor | null => {
const pos = this.konva.stage.getPointerPosition();
if (!pos) {
return null;
}
const ctx = this.konva.stage
.toCanvas({ x: pos.x, y: pos.y, width: 1, height: 1, imageSmoothingEnabled: false })
.getContext('2d');
if (!ctx) {
return null;
if (!relative || !absolute) {
return;
}
const [r, g, b, _a] = ctx.getImageData(0, 0, 1, 1).data;
if (r === undefined || g === undefined || b === undefined) {
return null;
}
return { r, g, b };
this.$cursorPos.set({ relative, absolute });
};
getClip = (
@@ -296,29 +285,42 @@ export class CanvasToolModule extends CanvasModuleBase {
getCanDraw = (): boolean => {
if (this.manager.stateApi.getRenderedEntityCount() === 0) {
return false;
} else if (this.manager.$isBusy.get()) {
return false;
} else if (!this.manager.stateApi.getSelectedEntityAdapter()?.$isInteractable.get()) {
return false;
} else {
return true;
}
if (this.manager.$isBusy.get()) {
return false;
}
const selectedEntity = this.manager.stateApi.getSelectedEntityAdapter();
if (!selectedEntity) {
return false;
}
if (!selectedEntity.$isInteractable.get()) {
return false;
}
return true;
};
onStagePointerEnter = async (e: KonvaEventObject<PointerEvent>) => {
try {
this.$lastPointerType.set(e.evt.pointerType);
if (!this.getCanDraw()) {
return;
}
const cursorPos = this.syncLastCursorPos();
this.syncCursorPositions();
const cursorPos = this.$cursorPos.get();
const isMouseDown = this.$isMouseDown.get();
const settings = this.manager.stateApi.getSettings();
const tool = this.$tool.get();
const selectedEntity = this.manager.stateApi.getSelectedEntityAdapter();
if (!cursorPos || !isMouseDown || !selectedEntity?.state.isEnabled || selectedEntity.state.isLocked) {
if (!cursorPos || !isMouseDown || !selectedEntity?.$isInteractable.get()) {
return;
}
@@ -328,7 +330,7 @@ export class CanvasToolModule extends CanvasModuleBase {
}
if (tool === 'brush') {
const normalizedPoint = offsetCoord(cursorPos, selectedEntity.state.position);
const normalizedPoint = offsetCoord(cursorPos.relative, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, settings.brushWidth);
if (e.evt.pointerType === 'pen' && settings.pressureSensitivity) {
await selectedEntity.bufferRenderer.setBuffer({
@@ -353,7 +355,7 @@ export class CanvasToolModule extends CanvasModuleBase {
}
if (tool === 'eraser') {
const normalizedPoint = offsetCoord(cursorPos, selectedEntity.state.position);
const normalizedPoint = offsetCoord(cursorPos.relative, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, settings.brushWidth);
if (selectedEntity.bufferRenderer.state && selectedEntity.bufferRenderer.hasBuffer()) {
selectedEntity.bufferRenderer.commitBuffer();
@@ -390,19 +392,19 @@ export class CanvasToolModule extends CanvasModuleBase {
return;
}
this.$isMouseDown.set(getIsPrimaryMouseDown(e));
const cursorPos = this.syncLastCursorPos();
const isMouseDown = getIsPrimaryMouseDown(e);
this.$isMouseDown.set(isMouseDown);
const cursorPos = this.$cursorPos.get();
const tool = this.$tool.get();
const settings = this.manager.stateApi.getSettings();
const isMouseDown = this.$isMouseDown.get();
const selectedEntity = this.manager.stateApi.getSelectedEntityAdapter();
if (!cursorPos || !isMouseDown || !selectedEntity?.state.isEnabled || selectedEntity?.state.isLocked) {
if (!cursorPos || !isMouseDown || !selectedEntity?.$isInteractable.get()) {
return;
}
const normalizedPoint = offsetCoord(cursorPos, selectedEntity.state.position);
const normalizedPoint = offsetCoord(cursorPos.relative, selectedEntity.state.position);
if (tool === 'brush') {
if (e.evt.pointerType === 'pen' && settings.pressureSensitivity) {
@@ -548,7 +550,7 @@ export class CanvasToolModule extends CanvasModuleBase {
const settings = this.manager.stateApi.getSettings();
if (tool === 'colorPicker') {
const color = this.getColorUnderCursor();
const color = this.$colorUnderCursor.get();
if (color) {
this.manager.stateApi.setColor({ ...settings.color, ...color });
}
@@ -556,8 +558,7 @@ export class CanvasToolModule extends CanvasModuleBase {
}
const selectedEntity = this.manager.stateApi.getSelectedEntityAdapter();
const isDrawable = selectedEntity?.state.isEnabled && !selectedEntity.state.isLocked;
if (!isDrawable) {
if (!selectedEntity?.$isInteractable.get()) {
return;
}
@@ -597,6 +598,18 @@ export class CanvasToolModule extends CanvasModuleBase {
}
};
syncColorUnderCursor = rafThrottle(() => {
const cursorPos = this.$cursorPos.get();
if (!cursorPos) {
return;
}
const color = getColorAtCoordinate(this.konva.stage, cursorPos.absolute);
if (color) {
this.$colorUnderCursor.set(color);
}
});
onStagePointerMove = async (e: KonvaEventObject<PointerEvent>) => {
try {
this.$lastPointerType.set(e.evt.pointerType);
@@ -605,22 +618,23 @@ export class CanvasToolModule extends CanvasModuleBase {
return;
}
this.syncCursorPositions();
const cursorPos = this.$cursorPos.get();
if (!cursorPos) {
return;
}
const tool = this.$tool.get();
const cursorPos = this.syncLastCursorPos();
if (tool === 'colorPicker') {
const color = this.getColorUnderCursor();
if (color) {
this.$colorUnderCursor.set(color);
}
return;
this.syncColorUnderCursor();
}
const isMouseDown = this.$isMouseDown.get();
const selectedEntity = this.manager.stateApi.getSelectedEntityAdapter();
const isDrawable = selectedEntity?.state.isEnabled && !selectedEntity.state.isLocked && cursorPos && isMouseDown;
if (!isDrawable) {
if (!isMouseDown || !selectedEntity?.$isInteractable.get()) {
return;
}
@@ -635,11 +649,11 @@ export class CanvasToolModule extends CanvasModuleBase {
if (tool === 'brush' && (bufferState.type === 'brush_line' || bufferState.type === 'brush_line_with_pressure')) {
const lastPoint = getLastPointOfLine(bufferState.points);
const minDistance = settings.brushWidth * this.config.BRUSH_SPACING_TARGET_SCALE;
if (!lastPoint || !isDistanceMoreThanMin(cursorPos, lastPoint, minDistance)) {
if (!lastPoint || !isDistanceMoreThanMin(cursorPos.relative, lastPoint, minDistance)) {
return;
}
const normalizedPoint = offsetCoord(cursorPos, selectedEntity.state.position);
const normalizedPoint = offsetCoord(cursorPos.relative, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, settings.brushWidth);
if (lastPoint.x === alignedPoint.x && lastPoint.y === alignedPoint.y) {
@@ -660,11 +674,11 @@ export class CanvasToolModule extends CanvasModuleBase {
) {
const lastPoint = getLastPointOfLine(bufferState.points);
const minDistance = settings.eraserWidth * this.config.BRUSH_SPACING_TARGET_SCALE;
if (!lastPoint || !isDistanceMoreThanMin(cursorPos, lastPoint, minDistance)) {
if (!lastPoint || !isDistanceMoreThanMin(cursorPos.relative, lastPoint, minDistance)) {
return;
}
const normalizedPoint = offsetCoord(cursorPos, selectedEntity.state.position);
const normalizedPoint = offsetCoord(cursorPos.relative, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, settings.eraserWidth);
if (lastPoint.x === alignedPoint.x && lastPoint.y === alignedPoint.y) {
@@ -680,7 +694,7 @@ export class CanvasToolModule extends CanvasModuleBase {
await selectedEntity.bufferRenderer.setBuffer(bufferState);
} else if (tool === 'rect' && bufferState.type === 'rect') {
const normalizedPoint = offsetCoord(cursorPos, selectedEntity.state.position);
const normalizedPoint = offsetCoord(cursorPos.relative, selectedEntity.state.position);
const alignedPoint = floorCoord(normalizedPoint);
bufferState.rect.width = Math.round(alignedPoint.x - bufferState.rect.x);
bufferState.rect.height = Math.round(alignedPoint.y - bufferState.rect.y);
@@ -783,7 +797,7 @@ export class CanvasToolModule extends CanvasModuleBase {
return;
}
if (e.key === 'Escape') {
if (e.key === KEY_ESCAPE) {
// Cancel shape drawing on escape
e.preventDefault();
const selectedEntity = this.manager.stateApi.getSelectedEntityAdapter();
@@ -798,7 +812,7 @@ export class CanvasToolModule extends CanvasModuleBase {
return;
}
if (e.key === ' ') {
if (e.key === KEY_SPACE) {
// Select the view tool on space key down
e.preventDefault();
this.$toolBuffer.set(this.$tool.get());
@@ -808,7 +822,7 @@ export class CanvasToolModule extends CanvasModuleBase {
return;
}
if (e.key === 'Alt') {
if (e.key === KEY_ALT) {
// Select the color picker on alt key down
e.preventDefault();
this.$toolBuffer.set(this.$tool.get());
@@ -825,7 +839,7 @@ export class CanvasToolModule extends CanvasModuleBase {
return;
}
if (e.key === ' ') {
if (e.key === KEY_SPACE) {
// Revert the tool to the previous tool on space key up
e.preventDefault();
this.revertToolBuffer();
@@ -833,7 +847,7 @@ export class CanvasToolModule extends CanvasModuleBase {
return;
}
if (e.key === 'Alt') {
if (e.key === KEY_ALT) {
// Revert the tool to the previous tool on alt key up
e.preventDefault();
this.revertToolBuffer();

View File

@@ -8,6 +8,7 @@ import { clamp } from 'lodash-es';
import { customAlphabet } from 'nanoid';
import type { StrokeOptions } from 'perfect-freehand';
import getStroke from 'perfect-freehand';
import type { RgbColor } from 'react-colorful';
import { assert } from 'tsafe';
/**
@@ -633,3 +634,27 @@ export const getPointerType = (e: KonvaEventObject<PointerEvent>): 'mouse' | 'pe
return 'touch';
};
/**
* Gets the color at the given coordinate on the stage.
* @param stage The konva stage.
* @param coord The coordinate to get the color at. This must be the _absolute_ coordinate on the stage.
* @returns The color under the coordinate, or null if there was a problem getting the color.
*/
export const getColorAtCoordinate = (stage: Konva.Stage, coord: Coordinate): RgbColor | null => {
const ctx = stage
.toCanvas({ x: coord.x, y: coord.y, width: 1, height: 1, imageSmoothingEnabled: false })
.getContext('2d');
if (!ctx) {
return null;
}
const [r, g, b, _a] = ctx.getImageData(0, 0, 1, 1).data;
if (r === undefined || g === undefined || b === undefined) {
return null;
}
return { r, g, b };
};

View File

@@ -0,0 +1,98 @@
import { Input, Text } from '@invoke-ai/ui-library';
import { useBoolean } from 'common/hooks/useBoolean';
import { withResultAsync } from 'common/util/result';
import { toast } from 'features/toast/toast';
import type { ChangeEvent, KeyboardEvent } from 'react';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useUpdateBoardMutation } from 'services/api/endpoints/boards';
import type { BoardDTO } from 'services/api/types';
type Props = {
board: BoardDTO;
isSelected: boolean;
};
export const BoardEditableTitle = memo(({ board, isSelected }: Props) => {
const { t } = useTranslation();
const isEditing = useBoolean(false);
const [localTitle, setLocalTitle] = useState(board.board_name);
const ref = useRef<HTMLInputElement>(null);
const [updateBoard, updateBoardResult] = useUpdateBoardMutation();
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setLocalTitle(e.target.value);
}, []);
const onBlur = useCallback(async () => {
const trimmedTitle = localTitle.trim();
isEditing.setFalse();
if (trimmedTitle.length === 0) {
setLocalTitle(board.board_name);
} else if (trimmedTitle !== board.board_name) {
setLocalTitle(trimmedTitle);
const result = await withResultAsync(() =>
updateBoard({ board_id: board.board_id, changes: { board_name: trimmedTitle } }).unwrap()
);
if (result.isErr()) {
setLocalTitle(board.board_name);
toast({
status: 'error',
title: t('boards.updateBoardError'),
});
} else {
setLocalTitle(result.value.board_name);
}
}
}, [board.board_id, board.board_name, isEditing, localTitle, updateBoard, t]);
const onKeyDown = useCallback(
(e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
onBlur();
} else if (e.key === 'Escape') {
setLocalTitle(board.board_name);
isEditing.setFalse();
}
},
[board.board_name, isEditing, onBlur]
);
useEffect(() => {
if (isEditing.isTrue) {
ref.current?.focus();
ref.current?.select();
}
}, [isEditing.isTrue]);
if (!isEditing.isTrue) {
return (
<Text
size="sm"
fontWeight="semibold"
userSelect="none"
color={isSelected ? 'base.100' : 'base.300'}
onDoubleClick={isEditing.setTrue}
cursor="text"
minW={16}
>
{localTitle}
</Text>
);
}
return (
<Input
ref={ref}
value={localTitle}
onChange={onChange}
onBlur={onBlur}
onKeyDown={onKeyDown}
variant="outline"
isDisabled={updateBoardResult.isLoading}
_focusVisible={{ borderWidth: 1, borderColor: 'invokeBlueAlpha.400', borderRadius: 'base' }}
/>
);
});
BoardEditableTitle.displayName = 'CanvasEntityTitleEdit';

View File

@@ -1,22 +1,12 @@
import type { SystemStyleObject } from '@invoke-ai/ui-library';
import {
Editable,
EditableInput,
EditablePreview,
Flex,
Icon,
Image,
Text,
Tooltip,
useDisclosure,
useEditableControls,
} from '@invoke-ai/ui-library';
import { Box, Flex, Icon, Image, Text, Tooltip } from '@invoke-ai/ui-library';
import { skipToken } from '@reduxjs/toolkit/query';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIDroppable from 'common/components/IAIDroppable';
import type { AddToBoardDropData } from 'features/dnd/types';
import { AutoAddBadge } from 'features/gallery/components/Boards/AutoAddBadge';
import BoardContextMenu from 'features/gallery/components/Boards/BoardContextMenu';
import { BoardEditableTitle } from 'features/gallery/components/Boards/BoardsList/BoardEditableTitle';
import { BoardTooltip } from 'features/gallery/components/Boards/BoardsList/BoardTooltip';
import {
selectAutoAddBoardId,
@@ -24,23 +14,12 @@ import {
selectSelectedBoardId,
} from 'features/gallery/store/gallerySelectors';
import { autoAddBoardIdChanged, boardIdSelected } from 'features/gallery/store/gallerySlice';
import type { MouseEvent, MouseEventHandler, MutableRefObject } from 'react';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArchiveBold, PiImageSquare } from 'react-icons/pi';
import { useUpdateBoardMutation } from 'services/api/endpoints/boards';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import type { BoardDTO } from 'services/api/types';
const editableInputStyles: SystemStyleObject = {
p: 0,
fontSize: 'md',
w: '100%',
_focusVisible: {
p: 0,
},
};
const _hover: SystemStyleObject = {
bg: 'base.850',
};
@@ -56,9 +35,6 @@ const GalleryBoard = ({ board, isSelected }: GalleryBoardProps) => {
const autoAddBoardId = useAppSelector(selectAutoAddBoardId);
const autoAssignBoardOnClick = useAppSelector(selectAutoAssignBoardOnClick);
const selectedBoardId = useAppSelector(selectSelectedBoardId);
const editingDisclosure = useDisclosure();
const [localBoardName, setLocalBoardName] = useState(board.board_name);
const onStartEditingRef = useRef<MouseEventHandler | undefined>(undefined);
const onClick = useCallback(() => {
if (selectedBoardId !== board.board_id) {
@@ -69,8 +45,6 @@ const GalleryBoard = ({ board, isSelected }: GalleryBoardProps) => {
}
}, [selectedBoardId, board.board_id, autoAssignBoardOnClick, autoAddBoardId, dispatch]);
const [updateBoard, { isLoading: isUpdateBoardLoading }] = useUpdateBoardMutation();
const droppableData: AddToBoardDropData = useMemo(
() => ({
id: board.board_id,
@@ -80,113 +54,42 @@ const GalleryBoard = ({ board, isSelected }: GalleryBoardProps) => {
[board.board_id]
);
const onSubmit = useCallback(
async (newBoardName: string) => {
if (!newBoardName.trim()) {
// empty strings are not allowed
setLocalBoardName(board.board_name);
} else if (newBoardName === board.board_name) {
// don't updated the board name if it hasn't changed
} else {
try {
const { board_name } = await updateBoard({
board_id: board.board_id,
changes: { board_name: newBoardName },
}).unwrap();
// update local state
setLocalBoardName(board_name);
} catch {
// revert on error
setLocalBoardName(board.board_name);
}
}
editingDisclosure.onClose();
},
[board.board_id, board.board_name, editingDisclosure, updateBoard]
);
const onChange = useCallback((newBoardName: string) => {
setLocalBoardName(newBoardName);
}, []);
const onDoubleClick = useCallback((e: MouseEvent<HTMLDivElement>) => {
if (onStartEditingRef.current) {
onStartEditingRef.current(e);
}
}, []);
return (
<BoardContextMenu board={board}>
{(ref) => (
<Tooltip label={<BoardTooltip board={board} />} openDelay={1000} placement="left" closeOnScroll p={2}>
<Flex
position="relative"
ref={ref}
onClick={onClick}
onDoubleClick={onDoubleClick}
w="full"
alignItems="center"
borderRadius="base"
cursor="pointer"
py={1}
ps={1}
pe={4}
gap={4}
bg={isSelected ? 'base.850' : undefined}
_hover={_hover}
h={12}
>
<CoverImage board={board} />
<Editable
as={Flex}
<Box position="relative" w="full" h={12}>
<BoardContextMenu board={board}>
{(ref) => (
<Tooltip label={<BoardTooltip board={board} />} openDelay={1000} placement="left" closeOnScroll p={2}>
<Flex
ref={ref}
onClick={onClick}
alignItems="center"
borderRadius="base"
cursor="pointer"
py={1}
ps={1}
pe={4}
gap={4}
flexGrow={1}
onEdit={editingDisclosure.onOpen}
value={localBoardName}
isDisabled={isUpdateBoardLoading}
submitOnBlur={true}
onChange={onChange}
onSubmit={onSubmit}
isPreviewFocusable={false}
fontSize="sm"
bg={isSelected ? 'base.850' : undefined}
_hover={_hover}
w="full"
h="full"
>
<EditablePreview
cursor="pointer"
p={0}
fontSize="sm"
textOverflow="ellipsis"
noOfLines={1}
w="fit-content"
wordBreak="break-all"
fontWeight={isSelected ? 'bold' : 'normal'}
/>
<EditableInput sx={editableInputStyles} />
<JankEditableHijack onStartEditingRef={onStartEditingRef} />
</Editable>
{autoAddBoardId === board.board_id && !editingDisclosure.isOpen && <AutoAddBadge />}
{board.archived && !editingDisclosure.isOpen && <Icon as={PiArchiveBold} fill="base.300" />}
{!editingDisclosure.isOpen && <Text variant="subtext">{board.image_count}</Text>}
<IAIDroppable data={droppableData} dropLabel={t('gallery.move')} />
</Flex>
</Tooltip>
)}
</BoardContextMenu>
<CoverImage board={board} />
<Flex w="full">
<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>
</Tooltip>
)}
</BoardContextMenu>
<IAIDroppable data={droppableData} dropLabel={t('gallery.move')} />
</Box>
);
};
const JankEditableHijack = memo((props: { onStartEditingRef: MutableRefObject<MouseEventHandler | undefined> }) => {
const editableControls = useEditableControls();
useEffect(() => {
props.onStartEditingRef.current = editableControls.getEditButtonProps().onClick;
}, [props, editableControls]);
return null;
});
JankEditableHijack.displayName = 'JankEditableHijack';
export default memo(GalleryBoard);
const CoverImage = ({ board }: { board: BoardDTO }) => {

View File

@@ -1,5 +1,5 @@
import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { Flex, Icon, Text, Tooltip } from '@invoke-ai/ui-library';
import { Box, Flex, Icon, Text, Tooltip } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIDroppable from 'common/components/IAIDroppable';
import type { RemoveFromBoardDropData } from 'features/dnd/types';
@@ -58,45 +58,52 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
}
return (
<NoBoardBoardContextMenu>
{(ref) => (
<Tooltip label={<BoardTooltip board={null} />} openDelay={1000} placement="left" closeOnScroll>
<Flex
position="relative"
ref={ref}
onClick={handleSelectBoard}
w="full"
alignItems="center"
borderRadius="base"
cursor="pointer"
py={1}
ps={1}
pe={4}
gap={4}
bg={isSelected ? 'base.850' : undefined}
_hover={_hover}
h={12}
>
<Flex w="10" justifyContent="space-around">
{/* iconified from public/assets/images/invoke-symbol-wht-lrg.svg */}
<Icon boxSize={8} opacity={1} stroke="base.500" viewBox="0 0 66 66" fill="none">
<path
d="M43.9137 16H63.1211V3H3.12109V16H22.3285L43.9137 50H63.1211V63H3.12109V50H22.3285"
strokeWidth="5"
/>
</Icon>
</Flex>
<Box position="relative" w="full" h={12}>
<NoBoardBoardContextMenu>
{(ref) => (
<Tooltip label={<BoardTooltip board={null} />} openDelay={1000} placement="left" closeOnScroll>
<Flex
ref={ref}
onClick={handleSelectBoard}
w="full"
h="full"
alignItems="center"
borderRadius="base"
cursor="pointer"
py={1}
ps={1}
pe={4}
gap={4}
bg={isSelected ? 'base.850' : undefined}
_hover={_hover}
>
<Flex w="10" justifyContent="space-around">
{/* iconified from public/assets/images/invoke-symbol-wht-lrg.svg */}
<Icon boxSize={8} opacity={1} stroke="base.500" viewBox="0 0 66 66" fill="none">
<path
d="M43.9137 16H63.1211V3H3.12109V16H22.3285L43.9137 50H63.1211V63H3.12109V50H22.3285"
strokeWidth="5"
/>
</Icon>
</Flex>
<Text fontSize="sm" fontWeight={isSelected ? 'bold' : 'normal'} noOfLines={1} flexGrow={1}>
{boardName}
</Text>
{autoAddBoardId === 'none' && <AutoAddBadge />}
<Text variant="subtext">{imagesTotal}</Text>
<IAIDroppable data={droppableData} dropLabel={t('gallery.move')} />
</Flex>
</Tooltip>
)}
</NoBoardBoardContextMenu>
<Text
fontSize="sm"
color={isSelected ? 'base.100' : 'base.300'}
fontWeight="semibold"
noOfLines={1}
flexGrow={1}
>
{boardName}
</Text>
{autoAddBoardId === 'none' && <AutoAddBadge />}
<Text variant="subtext">{imagesTotal}</Text>
</Flex>
</Tooltip>
)}
</NoBoardBoardContextMenu>
<IAIDroppable data={droppableData} dropLabel={t('gallery.move')} />
</Box>
);
});

View File

@@ -1,4 +1,4 @@
import { IconButton } from '@invoke-ai/ui-library';
import { IconButton, MenuItem } from '@invoke-ai/ui-library';
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { memo, useCallback } from 'react';
@@ -20,12 +20,17 @@ export const ImageMenuItemCopy = memo(() => {
return (
<IconButton
as={MenuItem}
icon={<PiCopyBold />}
aria-label={t('parameters.copyImage')}
tooltip={t('parameters.copyImage')}
onClickCapture={onClick}
variant="ghost"
variant="unstyled"
colorScheme="base"
w="min-content"
display="flex"
alignItems="center"
justifyContent="center"
/>
);
});

View File

@@ -1,4 +1,4 @@
import { IconButton } from '@invoke-ai/ui-library';
import { IconButton, MenuItem } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
@@ -17,12 +17,17 @@ export const ImageMenuItemDelete = memo(() => {
return (
<IconButton
as={MenuItem}
icon={<PiTrashSimpleBold />}
onClickCapture={onClick}
aria-label={t('gallery.deleteImage', { count: 1 })}
tooltip={t('gallery.deleteImage', { count: 1 })}
variant="ghost"
colorScheme="red"
variant="unstyled"
w="min-content"
display="flex"
alignItems="center"
justifyContent="center"
isDestructive
/>
);
});

View File

@@ -1,4 +1,4 @@
import { IconButton } from '@invoke-ai/ui-library';
import { IconButton, MenuItem } from '@invoke-ai/ui-library';
import { useDownloadImage } from 'common/hooks/useDownloadImage';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { memo, useCallback } from 'react';
@@ -16,12 +16,17 @@ export const ImageMenuItemDownload = memo(() => {
return (
<IconButton
as={MenuItem}
icon={<PiDownloadSimpleBold />}
aria-label={t('parameters.downloadImage')}
tooltip={t('parameters.downloadImage')}
variant="ghost"
colorScheme="base"
onClick={onClick}
variant="unstyled"
colorScheme="base"
w="min-content"
display="flex"
alignItems="center"
justifyContent="center"
/>
);
});

View File

@@ -1,4 +1,4 @@
import { IconButton } from '@invoke-ai/ui-library';
import { IconButton, MenuItem } from '@invoke-ai/ui-library';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -13,12 +13,17 @@ export const ImageMenuItemOpenInNewTab = memo(() => {
return (
<IconButton
as={MenuItem}
onClickCapture={onClick}
aria-label={t('common.openInNewTab')}
tooltip={t('common.openInNewTab')}
icon={<PiArrowSquareOutBold />}
variant="ghost"
variant="unstyled"
colorScheme="base"
w="min-content"
display="flex"
alignItems="center"
justifyContent="center"
/>
);
});

View File

@@ -1,4 +1,4 @@
import { IconButton } from '@invoke-ai/ui-library';
import { IconButton, MenuItem } from '@invoke-ai/ui-library';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { memo, useCallback } from 'react';
@@ -15,12 +15,17 @@ export const ImageMenuItemOpenInViewer = memo(() => {
return (
<IconButton
as={MenuItem}
icon={<PiArrowsOutBold />}
onClick={onClick}
onClickCapture={onClick}
aria-label={t('common.openInViewer')}
tooltip={t('common.openInViewer')}
variant="ghost"
variant="unstyled"
colorScheme="base"
w="min-content"
display="flex"
alignItems="center"
justifyContent="center"
/>
);
});

View File

@@ -1,4 +1,4 @@
import { IconButton } from '@invoke-ai/ui-library';
import { IconButton, MenuItem } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
@@ -23,13 +23,18 @@ export const ImageMenuItemSelectForCompare = memo(() => {
return (
<IconButton
as={MenuItem}
icon={<PiImagesBold />}
isDisabled={!maySelectForCompare}
onClick={onClick}
aria-label={t('gallery.selectForCompare')}
tooltip={t('gallery.selectForCompare')}
variant="ghost"
variant="unstyled"
colorScheme="base"
w="min-content"
display="flex"
alignItems="center"
justifyContent="center"
/>
);
});

View File

@@ -89,7 +89,7 @@ const ImageViewerCloseButton = memo(() => {
aria-label={t('gallery.closeViewer')}
icon={<PiXBold />}
variant="ghost"
onClick={imageViewer.close}
onPointerUp={imageViewer.close}
/>
);
});

View File

@@ -17,7 +17,7 @@ import { $isConnected } from 'services/events/stores';
import type { FieldComponentProps } from './types';
const ImageFieldInputComponent = (props: FieldComponentProps<ImageFieldInputInstance, ImageFieldInputTemplate>) => {
const { nodeId, field } = props;
const { nodeId, field, fieldTemplate } = props;
const dispatch = useAppDispatch();
const isConnected = useStore($isConnected);
const { currentData: imageDTO, isError } = useGetImageDTOQuery(field.value?.image_name ?? skipToken);
@@ -67,7 +67,17 @@ const ImageFieldInputComponent = (props: FieldComponentProps<ImageFieldInputInst
}, [handleReset, isConnected, isError]);
return (
<Flex className="nodrag" w="full" h="full" alignItems="center" justifyContent="center">
<Flex
className="nodrag"
w="full"
h="full"
alignItems="center"
justifyContent="center"
borderColor="error.500"
borderStyle="solid"
borderWidth={fieldTemplate.required && !field.value ? 1 : 0}
borderRadius="base"
>
<IAIDndImage
imageDTO={imageDTO}
droppableData={droppableData}
@@ -92,9 +102,11 @@ export default memo(ImageFieldInputComponent);
const UploadElement = memo(() => {
const { t } = useTranslation();
return (
<Text fontSize={16} fontWeight="semibold">
{t('gallery.dropOrUpload')}
</Text>
<Flex h={16} w="full" alignItems="center" justifyContent="center">
<Text fontSize={16} fontWeight="semibold">
{t('gallery.dropOrUpload')}
</Text>
</Flex>
);
});

View File

@@ -172,10 +172,10 @@ const initialConfigState: AppConfig = {
initial: 4,
sliderMin: 2,
sliderMax: 6,
numberInputMin: 2,
numberInputMax: 6,
numberInputMin: 1,
numberInputMax: 20,
fineStep: 0.1,
coarseStep: 1,
coarseStep: 0.5,
},
},
};

View File

@@ -1 +1 @@
__version__ = "5.1.0rc2"
__version__ = "5.1.0"