From f366640d46d197c7967bfa1c3389dacb033bb118 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 28 Feb 2025 10:33:15 +1000 Subject: [PATCH 01/12] fix(ui): invoke button not showing loading indicator on canvas tab On the Canvas tab, when we made the network request to enqueue a batch, we were immediately resetting the request. This effectively disabled RTKQ's tracking of the request - including the loading state. As a result, when you click the Invoke button on the Canvas tab, it didn't show a spinner, and it was not clear that anything was happening. The solution is simple - just await the enqueue request before resetting the tracking, same as we already did on the workflows and upscaling tabs. I also added some extra logging messages for enqueuing, so we get the same JS console logs for each tab on success or failure. --- .../listeners/enqueueRequestedLinear.ts | 17 ++++++++--------- .../listeners/enqueueRequestedNodes.ts | 8 ++++++++ .../listeners/enqueueRequestedUpscale.ts | 8 ++++++++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts index 66997785690..53301f6a9d4 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts @@ -3,6 +3,7 @@ import { enqueueRequested } from 'app/store/actions'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; import { extractMessageFromAssertionError } from 'common/util/extractMessageFromAssertionError'; import { withResult, withResultAsync } from 'common/util/result'; +import { parseify } from 'common/util/serialize'; import { $canvasManager } from 'features/controlLayers/store/ephemeral'; import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig'; import { buildFLUXGraph } from 'features/nodes/util/graph/generation/buildFLUXGraph'; @@ -13,7 +14,6 @@ import { toast } from 'features/toast/toast'; import { serializeError } from 'serialize-error'; import { enqueueMutationFixedCacheKeyOptions, queueApi } from 'services/api/endpoints/queue'; import { assert, AssertionError } from 'tsafe'; -import type { JsonObject } from 'type-fest'; const log = logger('generation'); @@ -80,16 +80,15 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening) const req = dispatch( queueApi.endpoints.enqueueBatch.initiate(prepareBatchResult.value, enqueueMutationFixedCacheKeyOptions) ); - req.reset(); - const enqueueResult = await withResultAsync(() => req.unwrap()); - - if (enqueueResult.isErr()) { - log.error({ error: serializeError(enqueueResult.error) }, 'Failed to enqueue batch'); - return; + try { + await req.unwrap(); + log.debug(parseify({ batchConfig: prepareBatchResult.value }), 'Enqueued batch'); + } catch (error) { + log.error({ error: serializeError(error) }, 'Failed to enqueue batch'); + } finally { + req.reset(); } - - log.debug({ batchConfig: prepareBatchResult.value } as JsonObject, 'Enqueued batch'); }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts index 6a14028ad91..838019c428b 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts @@ -1,5 +1,7 @@ +import { logger } from 'app/logging/logger'; import { enqueueRequested } from 'app/store/actions'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; +import { parseify } from 'common/util/serialize'; import { $templates } from 'features/nodes/store/nodesSlice'; import { selectNodesSlice } from 'features/nodes/store/selectors'; import { isBatchNode, isInvocationNode } from 'features/nodes/types/invocation'; @@ -7,9 +9,12 @@ import { buildNodesGraph } from 'features/nodes/util/graph/buildNodesGraph'; import { resolveBatchValue } from 'features/nodes/util/node/resolveBatchValue'; import { buildWorkflowWithValidation } from 'features/nodes/util/workflow/buildWorkflow'; import { groupBy } from 'lodash-es'; +import { serializeError } from 'serialize-error'; import { enqueueMutationFixedCacheKeyOptions, queueApi } from 'services/api/endpoints/queue'; import type { Batch, BatchConfig } from 'services/api/types'; +const log = logger('generation'); + export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) => { startAppListening({ predicate: (action): action is ReturnType => @@ -101,6 +106,9 @@ export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) = const req = dispatch(queueApi.endpoints.enqueueBatch.initiate(batchConfig, enqueueMutationFixedCacheKeyOptions)); try { await req.unwrap(); + log.debug(parseify({ batchConfig }), 'Enqueued batch'); + } catch (error) { + log.error({ error: serializeError(error) }, 'Failed to enqueue batch'); } finally { req.reset(); } diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedUpscale.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedUpscale.ts index 022fc99716f..62e70263158 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedUpscale.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedUpscale.ts @@ -1,9 +1,14 @@ +import { logger } from 'app/logging/logger'; import { enqueueRequested } from 'app/store/actions'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; +import { parseify } from 'common/util/serialize'; import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig'; import { buildMultidiffusionUpscaleGraph } from 'features/nodes/util/graph/buildMultidiffusionUpscaleGraph'; +import { serializeError } from 'serialize-error'; import { enqueueMutationFixedCacheKeyOptions, queueApi } from 'services/api/endpoints/queue'; +const log = logger('generation'); + export const addEnqueueRequestedUpscale = (startAppListening: AppStartListening) => { startAppListening({ predicate: (action): action is ReturnType => @@ -19,6 +24,9 @@ export const addEnqueueRequestedUpscale = (startAppListening: AppStartListening) const req = dispatch(queueApi.endpoints.enqueueBatch.initiate(batchConfig, enqueueMutationFixedCacheKeyOptions)); try { await req.unwrap(); + log.debug(parseify({ batchConfig }), 'Enqueued batch'); + } catch (error) { + log.error({ error: serializeError(error) }, 'Failed to enqueue batch'); } finally { req.reset(); } From fad6c67f01efd4b33b1f20124af9249f0145f1a2 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:05:42 +1000 Subject: [PATCH 02/12] fix(ui): workflow description cut off --- .../sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx index c16c7fcd9f9..d6e3817fedd 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx @@ -11,7 +11,7 @@ export const ActiveWorkflowDescription = memo(() => { } return ( - + {description} ); From d215829b91f62cde3624b09144c99db05e13524f Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:05:56 +1000 Subject: [PATCH 03/12] feat(ui): increase spacing in form builder view mode --- .../nodes/components/sidePanel/builder/ContainerElement.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx index 69755c98ca5..eb5cd55664e 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx @@ -50,7 +50,7 @@ const ContainerElement = memo(({ id }: { id: string }) => { ContainerElement.displayName = 'ContainerElementComponent'; const containerViewModeSx: SystemStyleObject = { - gap: 4, + gap: 6, '&[data-self-layout="column"]': { flexDir: 'column', alignItems: 'stretch', @@ -197,7 +197,7 @@ const rootViewModeSx: SystemStyleObject = { borderRadius: 'base', w: 'full', h: 'full', - gap: 4, + gap: 6, display: 'flex', flex: 1, maxW: '768px', @@ -232,6 +232,7 @@ RootContainerElementViewMode.displayName = 'RootContainerElementViewMode'; const rootEditModeSx: SystemStyleObject = { ...rootViewModeSx, + gap: 4, '&[data-is-dragging-over="true"]': { opacity: 1, bg: 'base.850', From 51d661023e813a28d669d6df6874d49043525e13 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:26:10 +1000 Subject: [PATCH 04/12] Revert "feat(ui): increase spacing in form builder view mode" This reverts commit 3766a3ba1e082f31bce09f794c47eb95cd76f1b1. --- .../nodes/components/sidePanel/builder/ContainerElement.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx index eb5cd55664e..69755c98ca5 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx @@ -50,7 +50,7 @@ const ContainerElement = memo(({ id }: { id: string }) => { ContainerElement.displayName = 'ContainerElementComponent'; const containerViewModeSx: SystemStyleObject = { - gap: 6, + gap: 4, '&[data-self-layout="column"]': { flexDir: 'column', alignItems: 'stretch', @@ -197,7 +197,7 @@ const rootViewModeSx: SystemStyleObject = { borderRadius: 'base', w: 'full', h: 'full', - gap: 6, + gap: 4, display: 'flex', flex: 1, maxW: '768px', @@ -232,7 +232,6 @@ RootContainerElementViewMode.displayName = 'RootContainerElementViewMode'; const rootEditModeSx: SystemStyleObject = { ...rootViewModeSx, - gap: 4, '&[data-is-dragging-over="true"]': { opacity: 1, bg: 'base.850', From 94771ea626ab1d92509910c23eb6b9f5222713e2 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:30:47 +1000 Subject: [PATCH 05/12] feat(ui): add auto-links to text, heading, field description and workflow descriptions --- invokeai/frontend/web/package.json | 2 ++ invokeai/frontend/web/pnpm-lock.yaml | 20 +++++++++++++++++++ .../web/src/common/components/linkify.ts | 17 ++++++++++++++++ .../ActiveWorkflowDescription.tsx | 6 ++++-- .../builder/HeadingElementContent.tsx | 5 ++++- .../NodeFieldElementDescriptionEditable.tsx | 8 +++++++- .../builder/NodeFieldElementViewMode.tsx | 8 +++++++- .../sidePanel/builder/TextElementContent.tsx | 5 ++++- 8 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 invokeai/frontend/web/src/common/components/linkify.ts diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json index 3ebbd8ebb4a..0141deb57b9 100644 --- a/invokeai/frontend/web/package.json +++ b/invokeai/frontend/web/package.json @@ -75,6 +75,8 @@ "idb-keyval": "^6.2.1", "jsondiffpatch": "^0.6.0", "konva": "^9.3.15", + "linkify-react": "^4.2.0", + "linkifyjs": "^4.2.0", "lodash-es": "^4.17.21", "lru-cache": "^11.0.1", "mtwist": "^1.0.2", diff --git a/invokeai/frontend/web/pnpm-lock.yaml b/invokeai/frontend/web/pnpm-lock.yaml index b81b3c23e5d..21282bca51d 100644 --- a/invokeai/frontend/web/pnpm-lock.yaml +++ b/invokeai/frontend/web/pnpm-lock.yaml @@ -74,6 +74,12 @@ dependencies: konva: specifier: ^9.3.15 version: 9.3.15 + linkify-react: + specifier: ^4.2.0 + version: 4.2.0(linkifyjs@4.2.0)(react@18.3.1) + linkifyjs: + specifier: ^4.2.0 + version: 4.2.0 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -6714,6 +6720,20 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: false + /linkify-react@4.2.0(linkifyjs@4.2.0)(react@18.3.1): + resolution: {integrity: sha512-dIcDGo+n4FP2FPIHDcqB7cUE+omkcEgQJpc7sNNP4+XZ9FUhFAkKjGnHMzsZM+B4yF93sK166z9K5cKTe/JpzA==} + peerDependencies: + linkifyjs: ^4.0.0 + react: '>= 15.0.0' + dependencies: + linkifyjs: 4.2.0 + react: 18.3.1 + dev: false + + /linkifyjs@4.2.0: + resolution: {integrity: sha512-pCj3PrQyATaoTYKHrgWRF3SJwsm61udVh+vuls/Rl6SptiDhgE7ziUIudAedRY9QEfynmM7/RmLEfPUyw1HPCw==} + dev: false + /liqe@3.8.0: resolution: {integrity: sha512-cZ1rDx4XzxONBTskSPBp7/KwJ9qbUdF8EPnY4VjKXwHF1Krz9lgnlMTh1G7kd+KtPYvUte1mhuZeQSnk7KiSBg==} engines: {node: '>=12.0'} diff --git a/invokeai/frontend/web/src/common/components/linkify.ts b/invokeai/frontend/web/src/common/components/linkify.ts new file mode 100644 index 00000000000..4ac639468f7 --- /dev/null +++ b/invokeai/frontend/web/src/common/components/linkify.ts @@ -0,0 +1,17 @@ +import type { SystemStyleObject } from '@invoke-ai/ui-library'; +import type { Opts as LinkifyOpts } from 'linkifyjs'; + +export const linkifySx: SystemStyleObject = { + a: { + fontWeight: 'semibold', + }, + 'a:hover': { + textDecoration: 'underline', + }, +}; + +export const linkifyOptions: LinkifyOpts = { + target: '_blank', + rel: 'noopener noreferrer', + validate: (value) => /^https?:\/\//.test(value), +}; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx index d6e3817fedd..4b31c85823a 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx @@ -1,6 +1,8 @@ import { Text } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { linkifyOptions, linkifySx } from 'common/components/linkify'; import { selectWorkflowDescription } from 'features/nodes/store/workflowSlice'; +import Linkify from 'linkify-react'; import { memo } from 'react'; export const ActiveWorkflowDescription = memo(() => { @@ -11,8 +13,8 @@ export const ActiveWorkflowDescription = memo(() => { } return ( - - {description} + + {description} ); }); diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementContent.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementContent.tsx index 144c7a121d4..dc89d50f8a9 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementContent.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementContent.tsx @@ -1,5 +1,7 @@ import type { HeadingProps, SystemStyleObject } from '@invoke-ai/ui-library'; import { Text } from '@invoke-ai/ui-library'; +import { linkifyOptions, linkifySx } from 'common/components/linkify'; +import Linkify from 'linkify-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -9,13 +11,14 @@ const headingSx: SystemStyleObject = { '&[data-is-empty="true"]': { opacity: 0.3, }, + ...linkifySx, }; export const HeadingElementContent = memo(({ content, ...rest }: { content: string } & HeadingProps) => { const { t } = useTranslation(); return ( - {content || t('workflows.builder.headingPlaceholder')} + {content || t('workflows.builder.headingPlaceholder')} ); }); diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementDescriptionEditable.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementDescriptionEditable.tsx index 63acf2918b7..fa49391c22c 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementDescriptionEditable.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementDescriptionEditable.tsx @@ -1,10 +1,12 @@ import { FormHelperText, Textarea } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { linkifyOptions, linkifySx } from 'common/components/linkify'; import { useEditable } from 'common/hooks/useEditable'; import { useInputFieldDescription } from 'features/nodes/hooks/useInputFieldDescription'; import { useInputFieldTemplate } from 'features/nodes/hooks/useInputFieldTemplate'; import { fieldDescriptionChanged } from 'features/nodes/store/nodesSlice'; import type { NodeFieldElement } from 'features/nodes/types/workflow'; +import Linkify from 'linkify-react'; import { memo, useCallback, useRef } from 'react'; export const NodeFieldElementDescriptionEditable = memo(({ el }: { el: NodeFieldElement }) => { @@ -36,7 +38,11 @@ export const NodeFieldElementDescriptionEditable = memo(({ el }: { el: NodeField }); if (!editable.isEditing) { - return {editable.value}; + return ( + + {editable.value} + + ); } return ( diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx index f028ea4da18..ecee18bfb6d 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx @@ -1,5 +1,6 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; import { Flex, FormControl, FormHelperText } from '@invoke-ai/ui-library'; +import { linkifyOptions, linkifySx } from 'common/components/linkify'; import { InputFieldRenderer } from 'features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer'; import { useContainerContext } from 'features/nodes/components/sidePanel/builder/contexts'; import { NodeFieldElementLabel } from 'features/nodes/components/sidePanel/builder/NodeFieldElementLabel'; @@ -7,6 +8,7 @@ import { useInputFieldDescription } from 'features/nodes/hooks/useInputFieldDesc import { useInputFieldTemplate } from 'features/nodes/hooks/useInputFieldTemplate'; import type { NodeFieldElement } from 'features/nodes/types/workflow'; import { NODE_FIELD_CLASS_NAME } from 'features/nodes/types/workflow'; +import Linkify from 'linkify-react'; import { memo, useMemo } from 'react'; const sx: SystemStyleObject = { @@ -43,7 +45,11 @@ export const NodeFieldElementViewMode = memo(({ el }: { el: NodeFieldElement }) settings={data.settings} /> - {showDescription && _description && {_description}} + {showDescription && _description && ( + + {_description} + + )} ); diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementContent.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementContent.tsx index d62b56de7c5..c81d2bddb36 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementContent.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementContent.tsx @@ -1,5 +1,7 @@ import type { SystemStyleObject, TextProps } from '@invoke-ai/ui-library'; import { Text } from '@invoke-ai/ui-library'; +import { linkifyOptions, linkifySx } from 'common/components/linkify'; +import Linkify from 'linkify-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -9,13 +11,14 @@ const textSx: SystemStyleObject = { '&[data-is-empty="true"]': { opacity: 0.3, }, + ...linkifySx, }; export const TextElementContent = memo(({ content, ...rest }: { content: string } & TextProps) => { const { t } = useTranslation(); return ( - {content || t('workflows.builder.textPlaceholder')} + {content || t('workflows.builder.textPlaceholder')} ); }); From ded8a8428425940c87c62d168d51b537de084b3f Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:33:38 +1000 Subject: [PATCH 06/12] feat(ui): increase spacing in form builder view mode --- .../components/sidePanel/builder/ContainerElement.tsx | 5 +++-- .../sidePanel/builder/NodeFieldElementViewMode.tsx | 11 ++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx index 69755c98ca5..8e9a119a5f8 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/ContainerElement.tsx @@ -50,7 +50,7 @@ const ContainerElement = memo(({ id }: { id: string }) => { ContainerElement.displayName = 'ContainerElementComponent'; const containerViewModeSx: SystemStyleObject = { - gap: 4, + gap: 2, '&[data-self-layout="column"]': { flexDir: 'column', alignItems: 'stretch', @@ -197,7 +197,7 @@ const rootViewModeSx: SystemStyleObject = { borderRadius: 'base', w: 'full', h: 'full', - gap: 4, + gap: 2, display: 'flex', flex: 1, maxW: '768px', @@ -232,6 +232,7 @@ RootContainerElementViewMode.displayName = 'RootContainerElementViewMode'; const rootEditModeSx: SystemStyleObject = { ...rootViewModeSx, + gap: 4, '&[data-is-dragging-over="true"]': { opacity: 1, bg: 'base.850', diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx index ecee18bfb6d..2e5ba213771 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx @@ -20,6 +20,9 @@ const sx: SystemStyleObject = { flex: '1 1 0', minW: 32, }, + '&[data-with-description="false"]': { + pb: 2, + }, }; export const NodeFieldElementViewMode = memo(({ el }: { el: NodeFieldElement }) => { @@ -35,7 +38,13 @@ export const NodeFieldElementViewMode = memo(({ el }: { el: NodeFieldElement }) ); return ( - + From cc36cfb6171538e26de27e7088a5e2c7c24b2220 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:42:14 +1000 Subject: [PATCH 07/12] feat(ui): reorg workflow menu buttons --- .../flow/panels/TopPanel/TopPanel.tsx | 16 +++++++-- .../sidePanel/NewWorkflowButton.tsx | 34 ------------------- .../ActiveWorkflowNameAndActions.tsx | 4 +-- .../WorkflowLibraryMenu/SettingsMenuItem.tsx | 18 ---------- .../WorkflowLibraryMenu.tsx | 12 +++---- 5 files changed, 20 insertions(+), 64 deletions(-) delete mode 100644 invokeai/frontend/web/src/features/nodes/components/sidePanel/NewWorkflowButton.tsx delete mode 100644 invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SettingsMenuItem.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopPanel.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopPanel.tsx index 42651bdbed2..e45eb937dc9 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopPanel.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopPanel.tsx @@ -1,16 +1,21 @@ -import { Flex, Spacer } from '@invoke-ai/ui-library'; +import { Flex, IconButton, Spacer } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; import AddNodeButton from 'features/nodes/components/flow/panels/TopPanel/AddNodeButton'; import ClearFlowButton from 'features/nodes/components/flow/panels/TopPanel/ClearFlowButton'; import SaveWorkflowButton from 'features/nodes/components/flow/panels/TopPanel/SaveWorkflowButton'; import UpdateNodesButton from 'features/nodes/components/flow/panels/TopPanel/UpdateNodesButton'; +import { useWorkflowEditorSettingsModal } from 'features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings'; import { WorkflowName } from 'features/nodes/components/sidePanel/WorkflowName'; import { selectWorkflowName } from 'features/nodes/store/workflowSlice'; -import WorkflowLibraryMenu from 'features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu'; import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { PiGearSixFill } from 'react-icons/pi'; const TopCenterPanel = () => { const name = useAppSelector(selectWorkflowName); + const modal = useWorkflowEditorSettingsModal(); + + const { t } = useTranslation(); return ( @@ -22,7 +27,12 @@ const TopCenterPanel = () => { - + } + onClick={modal.setTrue} + /> ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/NewWorkflowButton.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/NewWorkflowButton.tsx deleted file mode 100644 index 1fa11e2dbec..00000000000 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/NewWorkflowButton.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { IconButton } from '@invoke-ai/ui-library'; -import { useNewWorkflow } from 'features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog'; -import type { MouseEvent } from 'react'; -import { memo, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { PiFilePlusBold } from 'react-icons/pi'; - -export const NewWorkflowButton = memo(() => { - const newWorkflow = useNewWorkflow(); - - const { t } = useTranslation(); - - const onClickNewWorkflow = useCallback( - (e: MouseEvent) => { - // We need to stop the event from propagating to the parent element, else the click will open the list menu - e.stopPropagation(); - newWorkflow.createWithDialog(); - }, - [newWorkflow] - ); - - return ( - } - /> - ); -}); - -NewWorkflowButton.displayName = 'NewWorkflowButton'; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowNameAndActions.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowNameAndActions.tsx index 31bb655522f..3bb2dff02bb 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowNameAndActions.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowNameAndActions.tsx @@ -1,9 +1,9 @@ import { Flex, Spacer } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; -import { NewWorkflowButton } from 'features/nodes/components/sidePanel/NewWorkflowButton'; import { WorkflowListMenuTrigger } from 'features/nodes/components/sidePanel/WorkflowListMenu/WorkflowListMenuTrigger'; import { WorkflowViewEditToggleButton } from 'features/nodes/components/sidePanel/WorkflowViewEditToggleButton'; import { selectWorkflowMode } from 'features/nodes/store/workflowSlice'; +import { WorkflowLibraryMenu } from 'features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu'; import { memo } from 'react'; import SaveWorkflowButton from './SaveWorkflowButton'; @@ -17,7 +17,7 @@ export const ActiveWorkflowNameAndActions = memo(() => { {mode === 'edit' && } - + ); }); diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SettingsMenuItem.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SettingsMenuItem.tsx deleted file mode 100644 index 59699427951..00000000000 --- a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/SettingsMenuItem.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { MenuItem } from '@invoke-ai/ui-library'; -import { useWorkflowEditorSettingsModal } from 'features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { PiGearSixFill } from 'react-icons/pi'; - -const DownloadWorkflowMenuItem = () => { - const { t } = useTranslation(); - const modal = useWorkflowEditorSettingsModal(); - - return ( - } onClick={modal.setTrue}> - {t('nodes.workflowSettings')} - - ); -}; - -export default memo(DownloadWorkflowMenuItem); diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx index 73e9f5d4ba6..25da6bd4dea 100644 --- a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx +++ b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryMenu/WorkflowLibraryMenu.tsx @@ -13,13 +13,12 @@ import LoadWorkflowFromGraphMenuItem from 'features/workflowLibrary/components/W import { NewWorkflowMenuItem } from 'features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem'; import SaveWorkflowAsMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem'; import SaveWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowMenuItem'; -import SettingsMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/SettingsMenuItem'; import UploadWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/UploadWorkflowMenuItem'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { PiDotsThreeOutlineFill } from 'react-icons/pi'; -const WorkflowLibraryMenu = () => { +export const WorkflowLibraryMenu = memo(() => { const { t } = useTranslation(); const { isOpen, onOpen, onClose } = useDisclosure(); const shift = useShiftModifier(); @@ -31,6 +30,8 @@ const WorkflowLibraryMenu = () => { aria-label={t('workflows.workflowEditorMenu')} icon={} pointerEvents="auto" + size="sm" + variant="ghost" /> @@ -39,13 +40,10 @@ const WorkflowLibraryMenu = () => { - - {shift && } {shift && } ); -}; - -export default memo(WorkflowLibraryMenu); +}); +WorkflowLibraryMenu.displayName = 'WorkflowLibraryMenu'; From 82f645c7a1226549a2c65200acee68c987d5121a Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:46:03 +1000 Subject: [PATCH 08/12] feat(ui): add new workflow button to library menu --- .../WorkflowListMenuTrigger.tsx | 2 ++ .../components/NewWorkflowButton.tsx | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/WorkflowListMenuTrigger.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/WorkflowListMenuTrigger.tsx index 5e851242c36..c2fe14669f4 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/WorkflowListMenuTrigger.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/WorkflowListMenuTrigger.tsx @@ -15,6 +15,7 @@ import { useAppSelector } from 'app/store/storeHooks'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import { useWorkflowListMenu } from 'features/nodes/store/workflowListMenu'; import { selectWorkflowName } from 'features/nodes/store/workflowSlice'; +import { NewWorkflowButton } from 'features/workflowLibrary/components/NewWorkflowButton'; import UploadWorkflowButton from 'features/workflowLibrary/components/UploadWorkflowButton'; import { useRef } from 'react'; import { useTranslation } from 'react-i18next'; @@ -63,6 +64,7 @@ export const WorkflowListMenuTrigger = () => { + diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowButton.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowButton.tsx new file mode 100644 index 00000000000..e51ff78eb29 --- /dev/null +++ b/invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowButton.tsx @@ -0,0 +1,33 @@ +import { IconButton } from '@invoke-ai/ui-library'; +import { useNewWorkflow } from 'features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog'; +import type { MouseEvent } from 'react'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { PiFilePlusBold } from 'react-icons/pi'; + +export const NewWorkflowButton = memo(() => { + const newWorkflow = useNewWorkflow(); + + const { t } = useTranslation(); + + const onClickNewWorkflow = useCallback( + (e: MouseEvent) => { + // We need to stop the event from propagating to the parent element, else the click will open the list menu + e.stopPropagation(); + newWorkflow.createWithDialog(); + }, + [newWorkflow] + ); + + return ( + } + /> + ); +}); + +NewWorkflowButton.displayName = 'NewWorkflowButton'; From 3519e8ae39a78056a9f2b3ed5044be519de85629 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Fri, 28 Feb 2025 05:58:32 +0100 Subject: [PATCH 09/12] translationBot(ui): update translation files Updated by "Cleanup translation files" hook in Weblate. Co-authored-by: Hosted Weblate Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/ Translation: InvokeAI/Web UI --- invokeai/frontend/web/public/locales/it.json | 6 +----- invokeai/frontend/web/public/locales/vi.json | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/invokeai/frontend/web/public/locales/it.json b/invokeai/frontend/web/public/locales/it.json index 240320e9daf..a4421cb969e 100644 --- a/invokeai/frontend/web/public/locales/it.json +++ b/invokeai/frontend/web/public/locales/it.json @@ -2277,11 +2277,7 @@ "watchUiUpdatesOverview": "Guarda le novità dell'interfaccia", "items": [ "Impostazioni predefinite VRAM migliorate", - "Cancellazione della cache del modello su richiesta", - "Compatibilità estesa FLUX LoRA", - "Filtro Regola Immagine su Tela", - "Annulla tutto tranne l'elemento della coda corrente", - "Copia da e incolla sulla Tela" + "Cancellazione della cache del modello su richiesta" ] }, "system": { diff --git a/invokeai/frontend/web/public/locales/vi.json b/invokeai/frontend/web/public/locales/vi.json index e65aaa350c6..fc0dc33c3d4 100644 --- a/invokeai/frontend/web/public/locales/vi.json +++ b/invokeai/frontend/web/public/locales/vi.json @@ -2311,11 +2311,7 @@ "watchUiUpdatesOverview": "Xem Tổng Quan Về Những Cập Nhật Cho Giao Diện Người Dùng", "items": [ "Cải thiện các thiết lập mặc định của VRAM", - "Xoá bộ nhớ đệm của model theo yêu cầu", - "Mở rộng khả năng tương thích LoRA trên FLUX", - "Bộ lọc điều chỉnh ảnh trên Canvas", - "Huỷ tất cả trừ mục đang xếp hàng hiện tại", - "Sao chép và dán trên Canvas" + "Xoá bộ nhớ đệm của model theo yêu cầu" ] }, "upsell": { From 316ed1d478a21724b554d8c2a1ece1e2de362882 Mon Sep 17 00:00:00 2001 From: Hiroto N Date: Fri, 28 Feb 2025 05:58:34 +0100 Subject: [PATCH 10/12] translationBot(ui): update translation (Japanese) Currently translated at 42.6% (766 of 1797 strings) Co-authored-by: Hiroto N Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/ja/ Translation: InvokeAI/Web UI --- invokeai/frontend/web/public/locales/ja.json | 91 +++++++++++++++++--- 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/invokeai/frontend/web/public/locales/ja.json b/invokeai/frontend/web/public/locales/ja.json index c29bfa4af55..79fb2f8e481 100644 --- a/invokeai/frontend/web/public/locales/ja.json +++ b/invokeai/frontend/web/public/locales/ja.json @@ -329,7 +329,13 @@ "redo": { "title": "やり直し" }, - "title": "ワークフロー" + "title": "ワークフロー", + "pasteSelection": { + "title": "ペースト" + }, + "copySelection": { + "title": "コピー" + } }, "app": { "toggleLeftPanel": { @@ -390,7 +396,10 @@ "desc": "カーソルをポジティブプロンプト欄に移動します。" } }, - "hotkeys": "ホットキー" + "hotkeys": "ホットキー", + "gallery": { + "title": "ギャラリー" + } }, "modelManager": { "modelManager": "モデルマネージャ", @@ -452,7 +461,8 @@ "loraModels": "LoRA", "edit": "編集", "install": "インストール", - "huggingFacePlaceholder": "owner/model-name" + "huggingFacePlaceholder": "owner/model-name", + "variant": "Variant" }, "parameters": { "images": "画像", @@ -507,7 +517,8 @@ "resetWebUIDesc2": "もしギャラリーに画像が表示されないなど、何か問題が発生した場合はGitHubにissueを提出する前にリセットを試してください。", "resetComplete": "WebUIはリセットされました。", "ui": "ユーザーインターフェイス", - "beta": "ベータ" + "beta": "ベータ", + "developer": "開発者" }, "toast": { "uploadFailed": "アップロード失敗", @@ -556,7 +567,8 @@ "negativePrompt": "ネガティブプロンプト", "generationMode": "生成モード", "vae": "VAE", - "cfgRescaleMultiplier": "$t(parameters.cfgRescaleMultiplier)" + "cfgRescaleMultiplier": "$t(parameters.cfgRescaleMultiplier)", + "canvasV2Metadata": "キャンバス" }, "queue": { "queueEmpty": "キューが空です", @@ -690,7 +702,8 @@ "notes": "ノート", "workflow": "ワークフロー", "workflowName": "名前", - "workflowNotes": "ノート" + "workflowNotes": "ノート", + "enum": "Enum" }, "boards": { "autoAddBoard": "自動追加するボード", @@ -823,6 +836,15 @@ }, "lora": { "heading": "LoRA" + }, + "loraWeight": { + "heading": "重み" + }, + "patchmatchDownScaleSize": { + "heading": "Downscale" + }, + "controlNetWeight": { + "heading": "重み" } }, "accordions": { @@ -865,7 +887,8 @@ "queue": "キュー", "canvas": "キャンバス", "workflows": "ワークフロー", - "models": "モデル" + "models": "モデル", + "gallery": "ギャラリー" } }, "controlLayers": { @@ -880,7 +903,8 @@ "bboxGroup": "バウンディングボックスから作成", "cropCanvasToBbox": "キャンバスをバウンディングボックスでクロップ", "newGlobalReferenceImage": "新規全域参照画像", - "newRegionalReferenceImage": "新規領域参照画像" + "newRegionalReferenceImage": "新規領域参照画像", + "canvasGroup": "キャンバス" }, "regionalGuidance": "領域ガイダンス", "globalReferenceImage": "全域参照画像", @@ -901,7 +925,8 @@ "brush": "ブラシ", "rectangle": "矩形", "move": "移動", - "eraser": "消しゴム" + "eraser": "消しゴム", + "bbox": "Bbox" }, "saveCanvasToGallery": "キャンバスをギャラリーに保存", "saveBboxToGallery": "バウンディングボックスをギャラリーへ保存", @@ -919,7 +944,27 @@ "canvas": "キャンバス", "fitBboxToLayers": "バウンディングボックスをレイヤーにフィット", "removeBookmark": "ブックマークを外す", - "savedToGalleryOk": "ギャラリーに保存しました" + "savedToGalleryOk": "ギャラリーに保存しました", + "controlMode": { + "prompt": "プロンプト" + }, + "prompt": "プロンプト", + "settings": { + "snapToGrid": { + "off": "オフ", + "on": "オン" + } + }, + "filter": { + "filter": "フィルター", + "spandrel_filter": { + "model": "モデル" + }, + "apply": "適用", + "reset": "リセット", + "cancel": "キャンセル" + }, + "weight": "重み" }, "stylePresets": { "clearTemplateSelection": "選択したテンプレートをクリア", @@ -934,7 +979,10 @@ "toggleViewMode": "表示モードを切り替え", "negativePromptColumn": "'negative_prompt'", "preview": "プレビュー", - "nameColumn": "'name'" + "nameColumn": "'name'", + "type": "タイプ", + "private": "プライベート", + "name": "名称" }, "upscaling": { "upscaleModel": "アップスケールモデル", @@ -946,7 +994,8 @@ "denoisingStrength": "ノイズ除去強度", "scheduler": "スケジューラー", "loading": "ロード中...", - "steps": "ステップ" + "steps": "ステップ", + "refiner": "Refiner" }, "modelCache": { "clear": "モデルキャッシュを消去", @@ -958,5 +1007,23 @@ "ascending": "昇順", "name": "名前", "descending": "降順" + }, + "system": { + "logNamespaces": { + "system": "システム", + "gallery": "ギャラリー", + "workflows": "ワークフロー", + "models": "モデル", + "canvas": "キャンバス", + "metadata": "メタデータ", + "queue": "キュー" + }, + "logLevel": { + "debug": "Debug", + "info": "Info", + "error": "Error", + "fatal": "Fatal", + "warn": "Warn" + } } } From 52aa839b7ea9db39099449c66d1cc8e7fe72c61a Mon Sep 17 00:00:00 2001 From: Thomas Bolteau Date: Fri, 28 Feb 2025 05:58:35 +0100 Subject: [PATCH 11/12] translationBot(ui): update translation (French) Currently translated at 99.1% (1782 of 1797 strings) Co-authored-by: Thomas Bolteau Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/fr/ Translation: InvokeAI/Web UI --- invokeai/frontend/web/public/locales/fr.json | 235 ++++++++++++++++--- 1 file changed, 208 insertions(+), 27 deletions(-) diff --git a/invokeai/frontend/web/public/locales/fr.json b/invokeai/frontend/web/public/locales/fr.json index 68ddac8f7f0..0097ac8ed09 100644 --- a/invokeai/frontend/web/public/locales/fr.json +++ b/invokeai/frontend/web/public/locales/fr.json @@ -98,7 +98,22 @@ "close": "Fermer", "clipboard": "Presse-papier", "loadingModel": "Chargement du modèle", - "generating": "En Génération" + "generating": "En Génération", + "warnings": "Alertes", + "layout": "Disposition", + "row": "Ligne", + "column": "Colonne", + "start": "Commencer", + "board": "Planche", + "count": "Quantité", + "step": "Étape", + "end": "Fin", + "min": "Min", + "max": "Max", + "values": "Valeurs", + "resetToDefaults": "Réinitialiser par défaut", + "seed": "Graine", + "combinatorial": "Combinatoire" }, "gallery": { "galleryImageSize": "Taille de l'image", @@ -165,7 +180,9 @@ "imagesSettings": "Paramètres des images de la galerie", "assetsTab": "Fichiers que vous avez importés pour vos projets.", "imagesTab": "Images que vous avez créées et enregistrées dans Invoke.", - "boardsSettings": "Paramètres des planches" + "boardsSettings": "Paramètres des planches", + "assets": "Ressources", + "images": "Images" }, "modelManager": { "modelManager": "Gestionnaire de modèle", @@ -289,7 +306,7 @@ "usingDefaultSettings": "Utilisation des paramètres par défaut du modèle", "defaultSettingsOutOfSync": "Certain paramètres ne correspondent pas aux valeurs par défaut du modèle :", "restoreDefaultSettings": "Cliquez pour utiliser les paramètres par défaut du modèle.", - "hfForbiddenErrorMessage": "Nous vous recommandons de visiter la page du modèle sur HuggingFace.com. Le propriétaire peut exiger l'acceptation des conditions pour pouvoir télécharger.", + "hfForbiddenErrorMessage": "Nous vous recommandons de visiter la page du modèle. Le propriétaire peut exiger l'acceptation des conditions pour pouvoir télécharger.", "hfTokenRequired": "Vous essayez de télécharger un modèle qui nécessite un token HuggingFace valide.", "clipLEmbed": "CLIP-L Embed", "hfTokenSaved": "Token HF enregistré", @@ -303,7 +320,10 @@ "hfForbidden": "Vous n'avez pas accès à ce modèle HF.", "hfTokenInvalidErrorMessage2": "Mettre à jour dans le ", "controlLora": "Controle LoRA", - "urlUnauthorizedErrorMessage2": "Découvrir comment ici." + "urlUnauthorizedErrorMessage2": "Découvrir comment ici.", + "urlUnauthorizedErrorMessage": "Vous devrez peut-être configurer un jeton API pour accéder à ce modèle.", + "urlForbidden": "Vous n'avez pas accès à ce modèle", + "urlForbiddenErrorMessage": "Vous devrez peut-être demander l'autorisation du site qui distribue le modèle." }, "parameters": { "images": "Images", @@ -345,19 +365,31 @@ "fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), la hauteur de la bounding box est {{height}}", "fluxModelIncompatibleScaledBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), la hauteur de la bounding box est {{height}}", "noFLUXVAEModelSelected": "Aucun modèle VAE sélectionné pour la génération FLUX", - "canvasIsTransforming": "La Toile se transforme", - "canvasIsRasterizing": "La Toile se rastérise", + "canvasIsTransforming": "La Toile est occupée (en transformation)", + "canvasIsRasterizing": "La Toile est occupée (en rastérisation)", "noCLIPEmbedModelSelected": "Aucun modèle CLIP Embed sélectionné pour la génération FLUX", - "canvasIsFiltering": "La Toile est en train de filtrer", + "canvasIsFiltering": "La Toile est occupée (en filtration)", "fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), la largeur de la bounding box est {{width}}", "noT5EncoderModelSelected": "Aucun modèle T5 Encoder sélectionné pour la génération FLUX", "fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), la largeur de la bounding box mise à l'échelle est {{width}}", - "canvasIsCompositing": "La toile est en train de composer", - "collectionTooFewItems": "{{nodeLabel}} -> {{fieldLabel}} : trop peu d'éléments, minimum {{minItems}}", - "collectionTooManyItems": "{{nodeLabel}} -> {{fieldLabel}} : trop d'éléments, maximum {{maxItems}}", + "canvasIsCompositing": "La Toile est occupée (en composition)", + "collectionTooFewItems": "trop peu d'éléments, minimum {{minItems}}", + "collectionTooManyItems": "trop d'éléments, maximum {{maxItems}}", "canvasIsSelectingObject": "La toile est occupée (sélection d'objet)", "emptyBatches": "lots vides", - "batchNodeNotConnected": "Noeud de lots non connecté : {{label}}" + "batchNodeNotConnected": "Noeud de lots non connecté : {{label}}", + "fluxModelMultipleControlLoRAs": "Vous ne pouvez utiliser qu'un seul Control LoRA à la fois", + "collectionNumberLTMin": "{{value}} < {{minimum}} (incl. min)", + "collectionNumberGTMax": "{{value}} > {{maximum}} (incl. max)", + "collectionNumberGTExclusiveMax": "{{value}} >= {{exclusiveMaximum}} (max exc)", + "batchNodeEmptyCollection": "Certains nœuds de lot ont des collections vides", + "batchNodeCollectionSizeMismatch": "Non-concordance de taille de collection sur le lot {{batchGroupId}}", + "collectionStringTooLong": "trop long, max {{maxLength}}", + "collectionNumberNotMultipleOf": "{{value}} n'est pas un multiple de {{multipleOf}}", + "collectionEmpty": "collection vide", + "collectionStringTooShort": "trop court, min {{minLength}}", + "collectionNumberLTExclusiveMin": "{{value}} <= {{exclusiveMinimum}} (min exc)", + "batchNodeCollectionSizeMismatchNoGroupId": "Taille de collection de groupe par lot non conforme" }, "negativePromptPlaceholder": "Prompt Négatif", "positivePromptPlaceholder": "Prompt Positif", @@ -501,7 +533,13 @@ "uploadFailedInvalidUploadDesc_withCount_one": "Doit être au maximum une image PNG ou JPEG.", "uploadFailedInvalidUploadDesc_withCount_many": "Doit être au maximum {{count}} images PNG ou JPEG.", "uploadFailedInvalidUploadDesc_withCount_other": "Doit être au maximum {{count}} images PNG ou JPEG.", - "addedToUncategorized": "Ajouté aux ressources de la planche $t(boards.uncategorized)" + "addedToUncategorized": "Ajouté aux ressources de la planche $t(boards.uncategorized)", + "pasteSuccess": "Collé à {{destination}}", + "pasteFailed": "Échec du collage", + "outOfMemoryErrorDescLocal": "Suivez notre guide Low VRAM pour réduire les OOMs.", + "unableToCopy": "Incapable de Copier", + "unableToCopyDesc": "Votre navigateur ne prend pas en charge l'accès au presse-papiers. Les utilisateurs de Firefox peuvent peut-être résoudre ce problème en suivant ", + "unableToCopyDesc_theseSteps": "ces étapes" }, "accessibility": { "uploadImage": "Importer une image", @@ -659,7 +697,14 @@ "iterations_many": "Itérations", "iterations_other": "Itérations", "back": "fin", - "batchSize": "Taille de lot" + "batchSize": "Taille de lot", + "retryFailed": "Problème de nouvelle tentative de l'élément", + "retrySucceeded": "Élément Retenté", + "retryItem": "Réessayer l'élement", + "cancelAllExceptCurrentQueueItemAlertDialog": "Annuler tous les éléments de la file d'attente, sauf celui en cours, arrêtera les éléments en attente mais permettra à celui en cours de se terminer.", + "cancelAllExceptCurrentQueueItemAlertDialog2": "Êtes-vous sûr de vouloir annuler tous les éléments en attente dans la file d'attente ?", + "cancelAllExceptCurrentTooltip": "Annuler tout sauf l'élément actuel", + "confirm": "Confirmer" }, "prompt": { "noMatchingTriggers": "Pas de déclancheurs correspondants", @@ -1031,7 +1076,9 @@ "controlNetWeight": { "heading": "Poids", "paragraphs": [ - "Poids du Control Adapter. Un poids plus élevé aura un impact plus important sur l'image finale." + "Poids du Control Adapter. Un poids plus élevé aura un impact plus important sur l'image finale.", + "• Poids plus élevé (.75-2) : Crée un impact plus significatif sur le résultat final.", + "• Poids inférieur (0-.75) : Crée un impact plus faible sur le résultat final." ] }, "compositingMaskAdjustments": { @@ -1076,8 +1123,9 @@ "controlNetBeginEnd": { "heading": "Pourcentage de début / de fin d'étape", "paragraphs": [ - "La partie du processus de débruitage à laquelle le Control Adapter sera appliqué.", - "En général, les Control Adapter appliqués au début du processus guident la composition, tandis que les Control Adapter appliqués à la fin guident les détails." + "Ce paramètre détérmine quelle portion du processus de débruitage (génération) utilisera cette couche comme guide.", + "En général, les Control Adapter appliqués au début du processus guident la composition, tandis que les Control Adapter appliqués à la fin guident les détails.", + "• Étape de fin (%): Spécifie quand arrêter d'appliquer le guide de cette couche et revenir aux guides généraux du modèle et aux autres paramètres." ] }, "controlNetControlMode": { @@ -1442,7 +1490,8 @@ "showDynamicPrompts": "Afficher les Prompts dynamiques", "dynamicPrompts": "Prompts Dynamiques", "promptsPreview": "Prévisualisation des Prompts", - "loading": "Génération des Pompts Dynamiques..." + "loading": "Génération des Pompts Dynamiques...", + "promptsToGenerate": "Prompts à générer" }, "metadata": { "positivePrompt": "Prompt Positif", @@ -1653,7 +1702,22 @@ "internalDesc": "Cette invocation est utilisée internalement par Invoke. En fonction des mises à jours il est possible que des changements y soit effectués ou qu'elle soit supprimé sans prévention.", "splitOn": "Diviser sur", "generatorNoValues": "vide", - "addItem": "Ajouter un élément" + "addItem": "Ajouter un élément", + "specialDesc": "Cette invocation nécessite un traitement spécial dans l'application. Par exemple, les nœuds Batch sont utilisés pour mettre en file d'attente plusieurs graphes à partir d'un seul workflow.", + "unableToUpdateNode": "La mise à jour du nœud a échoué : nœud {{node}} de type {{type}} (peut nécessiter la suppression et la recréation).", + "deletedMissingNodeFieldFormElement": "Champ de formulaire manquant supprimé : nœud {{nodeId}} champ {{fieldName}}", + "nodeName": "Nom du nœud", + "description": "Description", + "loadWorkflowDesc": "Charger le workflow ?", + "missingSourceOrTargetNode": "Nœud source ou cible manquant", + "generatorImagesCategory": "Catégorie", + "generatorImagesFromBoard": "Images de la Planche", + "missingSourceOrTargetHandle": "Manque de gestionnaire source ou cible", + "loadingTemplates": "Chargement de {{name}}", + "loadWorkflowDesc2": "Votre workflow actuel contient des modifications non enregistrées.", + "generatorImages_one": "{{count}} image", + "generatorImages_many": "{{count}} images", + "generatorImages_other": "{{count}} images" }, "models": { "noMatchingModels": "Aucun modèle correspondant", @@ -1712,13 +1776,41 @@ "deleteWorkflow2": "Êtes-vous sûr de vouloir supprimer ce Workflow ? Cette action ne peut pas être annulé.", "download": "Télécharger", "copyShareLinkForWorkflow": "Copier le lien de partage pour le Workflow", - "delete": "Supprimer" + "delete": "Supprimer", + "builder": { + "component": "Composant", + "numberInput": "Entrée de nombre", + "slider": "Curseur", + "both": "Les deux", + "singleLine": "Ligne unique", + "multiLine": "Multi Ligne", + "headingPlaceholder": "En-tête vide", + "emptyRootPlaceholderEditMode": "Faites glisser un élément de formulaire ou un champ de nœud ici pour commencer.", + "emptyRootPlaceholderViewMode": "Cliquez sur Modifier pour commencer à créer un formulaire pour ce workflow.", + "containerPlaceholder": "Conteneur Vide", + "row": "Ligne", + "column": "Colonne", + "layout": "Mise en page", + "nodeField": "Champ de nœud", + "zoomToNode": "Zoomer sur le nœud", + "nodeFieldTooltip": "Pour ajouter un champ de nœud, cliquez sur le petit bouton plus sur le champ dans l'Éditeur de Workflow, ou faites glisser le champ par son nom dans le formulaire.", + "addToForm": "Ajouter au formulaire", + "label": "Étiquette", + "textPlaceholder": "Texte vide", + "builder": "Constructeur de Formulaire", + "resetAllNodeFields": "Réinitialiser tous les champs de nœud", + "deleteAllElements": "Supprimer tous les éléments de formulaire", + "workflowBuilderAlphaWarning": "Le constructeur de workflow est actuellement en version alpha. Il peut y avoir des changements majeurs avant la version stable.", + "showDescription": "Afficher la description" + }, + "openLibrary": "Ouvrir la Bibliothèque" }, "whatsNew": { "whatsNewInInvoke": "Quoi de neuf dans Invoke", "watchRecentReleaseVideos": "Regarder les vidéos des dernières versions", "items": [ - "FLUX Guidage Régional (bêta) : Notre version bêta de FLUX Guidage Régional est en ligne pour le contrôle des prompt régionaux." + "FLUX Guidage Régional (bêta) : Notre version bêta de FLUX Guidage Régional est en ligne pour le contrôle des prompt régionaux.", + "Autres améliorations : mise en file d'attente par lots plus rapide, meilleur redimensionnement, sélecteur de couleurs amélioré et nœuds de métadonnées." ], "readReleaseNotes": "Notes de version", "watchUiUpdatesOverview": "Aperçu des mises à jour de l'interface utilisateur" @@ -1832,7 +1924,49 @@ "cancel": "Annuler", "advanced": "Avancé", "processingLayerWith": "Calque de traitement avec le filtre {{type}}.", - "forMoreControl": "Pour plus de contrôle, cliquez sur Avancé ci-dessous." + "forMoreControl": "Pour plus de contrôle, cliquez sur Avancé ci-dessous.", + "adjust_image": { + "b": "B (LAB)", + "blue": "Bleu (RGBA)", + "alpha": "Alpha (RGBA)", + "magenta": "Magenta (CMJN)", + "yellow": "Jaune (CMJN)", + "cb": "Cb (YCbCr)", + "cr": "Cr (YCbCr)", + "cyan": "Cyan (CMJN)", + "label": "Ajuster l'image", + "description": "Ajuste le canal sélectionné d'une image.", + "channel": "Canal", + "value_setting": "Valeur", + "scale_values": "Valeurs d'échelle", + "red": "Rouge (RGBA)", + "green": "Vert (RGBA)", + "black": "Noir (CMJN)", + "hue": "Teinte (HSV)", + "saturation": "Saturation (HSV)", + "value": "Valeur (HSV)", + "luminosity": "Luminosité (LAB)", + "a": "A (LAB)", + "y": "Y (YCbCr)" + }, + "img_blur": { + "label": "Flou de l'image", + "blur_type": "Type de flou", + "box_type": "Boîte", + "description": "Floute la couche sélectionnée.", + "blur_radius": "Rayon", + "gaussian_type": "Gaussien" + }, + "img_noise": { + "label": "Image de bruit", + "description": "Ajoute du bruit à la couche sélectionnée.", + "gaussian_type": "Gaussien", + "size": "Taille du bruit", + "noise_amount": "Quantité", + "noise_type": "Type de bruit", + "salt_and_pepper_type": "Sel et Poivre", + "noise_color": "Bruit coloré" + } }, "canvasContextMenu": { "saveToGalleryGroup": "Enregistrer dans la galerie", @@ -1846,7 +1980,10 @@ "newGlobalReferenceImage": "Nouvelle image de référence globale", "newControlLayer": "Nouveau couche de contrôle", "newInpaintMask": "Nouveau Masque Inpaint", - "newRegionalGuidance": "Nouveau Guide Régional" + "newRegionalGuidance": "Nouveau Guide Régional", + "copyToClipboard": "Copier dans le presse-papiers", + "copyBboxToClipboard": "Copier Bbox dans le presse-papiers", + "copyCanvasToClipboard": "Copier la Toile dans le presse-papiers" }, "bookmark": "Marque-page pour Changement Rapide", "saveLayerToAssets": "Enregistrer la couche dans les ressources", @@ -2012,7 +2149,10 @@ "ipAdapterMethod": "Méthode d'IP Adapter", "full": "Complet", "style": "Style uniquement", - "composition": "Composition uniquement" + "composition": "Composition uniquement", + "fullDesc": "Applique le style visuel (couleurs, textures) et la composition (mise en page, structure).", + "styleDesc": "Applique un style visuel (couleurs, textures) sans tenir compte de sa mise en page.", + "compositionDesc": "Réplique la mise en page et la structure tout en ignorant le style de la référence." }, "fitBboxToLayers": "Ajuster la bounding box aux calques", "regionIsEmpty": "La zone sélectionnée est vide", @@ -2095,7 +2235,40 @@ "asRasterLayerResize": "En tant que $t(controlLayers.rasterLayer) (Redimensionner)", "asControlLayer": "En tant que $t(controlLayers.controlLayer)", "asControlLayerResize": "En $t(controlLayers.controlLayer) (Redimensionner)", - "newSession": "Nouvelle session" + "newSession": "Nouvelle session", + "warnings": { + "controlAdapterIncompatibleBaseModel": "modèle de base de la couche de contrôle incompatible", + "controlAdapterNoControl": "aucun contrôle sélectionné/dessiné", + "rgNoPromptsOrIPAdapters": "pas de textes d'instructions ni d'images de référence", + "rgAutoNegativeNotSupported": "Auto-négatif non pris en charge pour le modèle de base sélectionné", + "rgNoRegion": "aucune région dessinée", + "ipAdapterNoModelSelected": "aucun modèle d'image de référence sélectionné", + "rgReferenceImagesNotSupported": "Les images de référence régionales ne sont pas prises en charge pour le modèle de base sélectionné", + "problemsFound": "Problèmes trouvés", + "unsupportedModel": "couche non prise en charge pour le modèle de base sélectionné", + "rgNegativePromptNotSupported": "Prompt négatif non pris en charge pour le modèle de base sélectionné", + "ipAdapterIncompatibleBaseModel": "modèle de base d'image de référence incompatible", + "controlAdapterNoModelSelected": "aucun modèle de couche de contrôle sélectionné", + "ipAdapterNoImageSelected": "Aucune image de référence sélectionnée." + }, + "pasteTo": "Coller vers", + "pasteToAssets": "Ressources", + "pasteToAssetsDesc": "Coller dans les ressources", + "pasteToBbox": "Bbox", + "regionCopiedToClipboard": "{{region}} Copié dans le presse-papiers", + "copyRegionError": "Erreur de copie {{region}}", + "pasteToCanvas": "Toile", + "errors": { + "unableToFindImage": "Impossible de trouver l'image", + "unableToLoadImage": "Impossible de charger l'image" + }, + "referenceImageRegional": "Image de référence (régionale)", + "pasteToBboxDesc": "Nouvelle couche (dans Bbox)", + "pasteToCanvasDesc": "Nouvelle couche (dans la Toile)", + "useImage": "Utiliser l'image", + "pastedTo": "Collé à {{destination}}", + "referenceImageEmptyState": "Séléctionner une image ou faites glisser une image depuis la galerie sur cette couche pour commencer.", + "referenceImageGlobal": "Image de référence (Globale)" }, "upscaling": { "exceedsMaxSizeDetails": "La limite maximale d'agrandissement est de {{maxUpscaleDimension}}x{{maxUpscaleDimension}} pixels. Veuillez essayer une image plus petite ou réduire votre sélection d'échelle.", @@ -2175,7 +2348,8 @@ "queue": "File d'attente", "events": "Événements", "metadata": "Métadonnées", - "gallery": "Galerie" + "gallery": "Galerie", + "dnd": "Glisser et déposer" }, "logLevel": { "trace": "Trace", @@ -2192,7 +2366,8 @@ "toGetStarted": "Pour commencer, saisissez un prompt dans la boîte et cliquez sur Invoke pour générer votre première image. Sélectionnez un template de prompt pour améliorer les résultats. Vous pouvez choisir de sauvegarder vos images directement dans la Galerie ou de les modifier sur la Toile.", "gettingStartedSeries": "Vous souhaitez plus de conseils ? Consultez notre Série de démarrage pour des astuces sur l'exploitation du plein potentiel de l'Invoke Studio.", "noModelsInstalled": "Il semble qu'aucun modèle ne soit installé", - "toGetStartedLocal": "Pour commencer, assurez-vous de télécharger ou d'importer des modèles nécessaires pour exécuter Invoke. Ensuite, saisissez le prompt dans la boîte et cliquez sur Invoke pour générer votre première image. Sélectionnez un template de prompt pour améliorer les résultats. Vous pouvez choisir de sauvegarder vos images directement sur Galerie ou les modifier sur la Toile." + "toGetStartedLocal": "Pour commencer, assurez-vous de télécharger ou d'importer des modèles nécessaires pour exécuter Invoke. Ensuite, saisissez le prompt dans la boîte et cliquez sur Invoke pour générer votre première image. Sélectionnez un template de prompt pour améliorer les résultats. Vous pouvez choisir de sauvegarder vos images directement sur Galerie ou les modifier sur la Toile.", + "lowVRAMMode": "Pour de meilleures performances, suivez notre guide Low VRAM." }, "upsell": { "shareAccess": "Partager l'accès", @@ -2240,7 +2415,8 @@ "description": "Introduction à l'ajout d'images de référence et IP Adapters globaux." }, "howDoIUseInpaintMasks": { - "title": "Comment utiliser les masques d'inpainting ?" + "title": "Comment utiliser les masques d'inpainting ?", + "description": "Comment appliquer des masques de retourche pour la correction et la variation d'image." }, "creatingYourFirstImage": { "title": "Créer votre première image", @@ -2260,5 +2436,10 @@ "studioSessionsDesc2": "Rejoignez notre pour participer aux sessions en direct et poser vos questions. Les sessions sont ajoutée dans la playlist la semaine suivante.", "supportVideos": "Vidéos d'assistance", "controlCanvas": "Contrôler la toile" + }, + "modelCache": { + "clear": "Effacer le cache du modèle", + "clearSucceeded": "Cache du modèle effacée", + "clearFailed": "Problème de nettoyage du cache du modèle" } } From 84c9ecc83f679fbb37be2d82854a3a6580811869 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:59:21 +1100 Subject: [PATCH 12/12] chore: bump version to v5.7.1 --- invokeai/version/invokeai_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/version/invokeai_version.py b/invokeai/version/invokeai_version.py index 7f4ce2d4c31..28a77fe053e 100644 --- a/invokeai/version/invokeai_version.py +++ b/invokeai/version/invokeai_version.py @@ -1 +1 @@ -__version__ = "5.7.0" +__version__ = "5.7.1"