Compare commits

..

29 Commits

Author SHA1 Message Date
8c6a8d072d remove tab character 2023-12-15 09:35:06 -05:00
ec52f15f4b add frontend build steps to pypi workflow 2023-12-15 09:30:37 -05:00
454f01e0c1 [feature] add ability to filter model listings by format (#5286)
## What type of PR is this? (check all applicable)

- [ ] Refactor
- [X] Feature
- [ ] Bug Fix
- [ ] Optimization
- [ ] Documentation Update
- [ ] Community Node Submission


## Have you discussed this change with the InvokeAI team?
- [X] Yes
- [ ] No, because:

      
## Have you updated all relevant documentation?
- [X] Yes
- [ ] No


## Description

This minor change adds the ability to filter the model lists returned by
V2 of the model manager using the model file format (e.g. "checkpoint").
Just thought this would be a useful feature.

## Related Tickets & Documents

<!--
For pull requests that relate or close an issue, please include them
below. 

For example having the text: "closes #1234" would connect the current
pull
request to issue 1234.  And when we merge the pull request, Github will
automatically close the issue.
-->

- Related Issue #
- Closes #

## QA Instructions, Screenshots, Recordings

<!-- 
Please provide steps on how to test changes, any hardware or 
software specifications as well as any other pertinent information. 
-->

## Merge Plan

This can be merged when approved without any adverse effects.

<!--
A merge plan describes how this PR should be handled after it is
approved.

Example merge plans:
- "This PR can be merged when approved"
- "This must be squash-merged when approved"
- "DO NOT MERGE - I will rebase and tidy commits before merging"
- "#dev-chat on discord needs to be advised of this change when it is
merged"

A merge plan is particularly important for large PRs or PRs that touch
the
database in any way.
-->

## Added/updated tests?

- [ ] Yes
- [X] No : minor feature - tested informally using the router API

## [optional] Are there any post deployment tasks we need to perform?
2023-12-15 00:03:01 -05:00
72dca55e44 Merge branch 'feat/model_manager/search-by-format' of github.com:invoke-ai/InvokeAI into feat/model_manager/search-by-format 2023-12-14 23:55:08 -05:00
264ea6d94d fix ruff errors 2023-12-14 23:54:59 -05:00
60e3e653fa Merge branch 'main' into feat/model_manager/search-by-format 2023-12-14 23:53:54 -05:00
082894c377 Adding Kapa Assistant to Docs (#5290)
## What type of PR is this? (check all applicable)

- [ ] Refactor
- [ ] Feature
- [ ] Bug Fix
- [ ] Optimization
- [ x ] Documentation Update
- [ ] Community Node Submission


## Have you discussed this change with the InvokeAI team?
- [ x ] Yes
- [ ] No, because:

      
## Have you updated all relevant documentation?
- [ x ] Yes
- [ ] No


## Description
This adds the Kapa assistant to our docs.
2023-12-15 09:47:40 +11:00
4b00f8fc82 Merge branch 'main' into Adding-Kapa-assistant-to-docs 2023-12-15 09:46:25 +11:00
6ea09ba0b6 feat(ui): workflow menu tweaks
- "Reset Workflow Editor" -> "New Workflow"
- "New Workflow" gets nodes icon & is no longer danger coloured
- When creating a new workflow, if the current workflow has unsaved changes, you get a dialog asking for confirmation. If the current workflow is saved, it immediately creates a new workflow.
- "Download Workflow" -> "Save to File"
- "Upload Workflow" -> "Load from File"
- Moved "Load from File" up 1 in the menu
2023-12-14 08:30:59 -05:00
42c04db167 adding kapa widget to docs 2023-12-13 22:33:50 -05:00
b935768eeb Update mkdocs.yml 2023-12-13 22:28:47 -05:00
ea4ef042f3 Ruff fixes 2023-12-14 12:47:10 +11:00
18b2bcbbee Added Classification from baseinvocation 2023-12-14 12:47:10 +11:00
5ad88c7f86 Fixed classification 2023-12-14 12:47:10 +11:00
3b04fef31d Added classification 2023-12-14 12:47:10 +11:00
bec888923a Fix for ruff 2023-12-14 12:47:10 +11:00
c6235049c7 Add an unsharp mask node to core nodes
Unsharp mask is an image operation that, despite its name, sharpens an image. Like a Gaussian blur, it takes a radius and strength.
2023-12-14 12:47:10 +11:00
e10f6e8962 fix(nodes): mark CalculateImageTilesInvocation as beta
missed this when I added classification
2023-12-13 20:33:25 -05:00
77f04ff8d6 docs: add warning to developer install about database & main 2023-12-14 11:47:33 +11:00
461e474394 fix(nodes): fix embedded workflows with IDs
This model was a bit too strict, and raised validation errors when workflows we expect to *not* have an ID (eg, an embedded workflow) have one.

Now it strips unknown attributes, allowing those workflows to load.
2023-12-14 11:38:04 +11:00
f0c70fe3f1 fix(db): add error handling for workflow migration
- Handle an image file not existing despite being in the database.
- Add a simple pydantic model that tests only for the existence of a workflow's version.
- Check against this new model when migrating workflows, skipping if the workflow fails validation. If it succeeds, the frontend should be able to handle the workflow.
2023-12-14 10:16:56 +11:00
442ac2b828 fix(ui): fix frontend workflow migration when node is missing version
This should default to "1.0.0" to match the behaviour of the backend.
2023-12-14 09:59:11 +11:00
bb986b97f3 translationBot(ui): update translation (Chinese (Simplified))
Currently translated at 99.8% (1363 of 1365 strings)

Co-authored-by: Surisen <zhonghx0804@outlook.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/zh_Hans/
Translation: InvokeAI/Web UI
2023-12-13 17:11:45 -05:00
98655db57b translationBot(ui): update translation (Russian)
Currently translated at 98.1% (1340 of 1365 strings)

translationBot(ui): update translation (Russian)

Currently translated at 84.2% (1150 of 1365 strings)

translationBot(ui): update translation (Russian)

Currently translated at 83.1% (1135 of 1365 strings)

Co-authored-by: Васянатор <ilabulanov339@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/ru/
Translation: InvokeAI/Web UI
2023-12-13 17:11:45 -05:00
8845894e83 translationBot(ui): update translation (Italian)
Currently translated at 97.0% (1325 of 1365 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
2023-12-13 17:11:45 -05:00
937c7e957d add merge plan to PR template 2023-12-13 16:59:31 -05:00
569ae7c482 add ability to filter model listings by format 2023-12-13 15:59:21 -05:00
340957f920 Update torch to 2.1.1 and xformers to 0.0.23 2023-12-13 14:49:32 -05:00
076d9b05ea Update transformers to 4.36 and Accelerate to 0.25 2023-12-13 14:42:34 -05:00
21 changed files with 1260 additions and 109 deletions

View File

@ -42,6 +42,21 @@ Please provide steps on how to test changes, any hardware or
software specifications as well as any other pertinent information. software specifications as well as any other pertinent information.
--> -->
## Merge Plan
<!--
A merge plan describes how this PR should be handled after it is approved.
Example merge plans:
- "This PR can be merged when approved"
- "This must be squash-merged when approved"
- "DO NOT MERGE - I will rebase and tidy commits before merging"
- "#dev-chat on discord needs to be advised of this change when it is merged"
A merge plan is particularly important for large PRs or PRs that touch the
database in any way.
-->
## Added/updated tests? ## Added/updated tests?
- [ ] Yes - [ ] Yes

View File

@ -15,19 +15,37 @@ jobs:
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
TWINE_NON_INTERACTIVE: 1 TWINE_NON_INTERACTIVE: 1
steps: steps:
- name: checkout sources - name: Checkout sources
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: install deps - name: Setup Node 20
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install pnpm dependencies
working-directory: invokeai/frontend/web
run: 'pnpm install --prefer-frozen-lockfile'
- name: Build frontend
working-directory: invokeai/frontend/web
run: 'pnpm build'
- name: Install python deps
run: pip install --upgrade build twine run: pip install --upgrade build twine
- name: build package - name: Build wheel package
run: python3 -m build run: python3 -m build
- name: check distribution - name: Check distribution
run: twine check dist/* run: twine check dist/*
- name: check PyPI versions - name: Check PyPI versions
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')
run: | run: |
pip install --upgrade requests pip install --upgrade requests
@ -36,6 +54,6 @@ jobs:
EXISTS=scripts.pypi_helper.local_on_pypi(); \ EXISTS=scripts.pypi_helper.local_on_pypi(); \
print(f'PACKAGE_EXISTS={EXISTS}')" >> $GITHUB_ENV print(f'PACKAGE_EXISTS={EXISTS}')" >> $GITHUB_ENV
- name: upload package - name: Upload package
if: env.PACKAGE_EXISTS == 'False' && env.TWINE_PASSWORD != '' if: env.PACKAGE_EXISTS == 'False' && env.TWINE_PASSWORD != ''
run: twine upload dist/* run: twine upload dist/*

View File

@ -293,6 +293,19 @@ manager, please follow these steps:
## Developer Install ## Developer Install
!!! warning
InvokeAI uses a SQLite database. By running on `main`, you accept responsibility for your database. This
means making regular backups (especially before pulling) and/or fixing it yourself in the event that a
PR introduces a schema change.
If you don't need persistent backend storage, you can use an ephemeral in-memory database by setting
`use_memory_db: true` under `Path:` in your `invokeai.yaml` file.
If this is untenable, you should run the application via the official installer or a manual install of the
python package from pypi. These releases will not break your database.
If you have an interest in how InvokeAI works, or you would like to If you have an interest in how InvokeAI works, or you would like to
add features or bugfixes, you are encouraged to install the source add features or bugfixes, you are encouraged to install the source
code for InvokeAI. For this to work, you will need to install the code for InvokeAI. For this to work, you will need to install the
@ -388,3 +401,5 @@ environment variable INVOKEAI_ROOT to point to the installation directory.
Note that if you run into problems with the Conda installation, the InvokeAI Note that if you run into problems with the Conda installation, the InvokeAI
staff will **not** be able to help you out. Caveat Emptor! staff will **not** be able to help you out. Caveat Emptor!
[dev-chat]: https://discord.com/channels/1020123559063990373/1049495067846524939

View File

@ -0,0 +1,10 @@
document.addEventListener("DOMContentLoaded", function () {
var script = document.createElement("script");
script.src = "https://widget.kapa.ai/kapa-widget.bundle.js";
script.setAttribute("data-website-id", "b5973bb1-476b-451e-8cf4-98de86745a10");
script.setAttribute("data-project-name", "Invoke.AI");
script.setAttribute("data-project-color", "#11213C");
script.setAttribute("data-project-logo", "https://avatars.githubusercontent.com/u/113954515?s=280&v=4");
script.async = true;
document.head.appendChild(script);
});

View File

@ -244,9 +244,9 @@ class InvokeAiInstance:
"numpy~=1.24.0", # choose versions that won't be uninstalled during phase 2 "numpy~=1.24.0", # choose versions that won't be uninstalled during phase 2
"urllib3~=1.26.0", "urllib3~=1.26.0",
"requests~=2.28.0", "requests~=2.28.0",
"torch==2.1.0", "torch==2.1.1",
"torchmetrics==0.11.4", "torchmetrics==0.11.4",
"torchvision>=0.14.1", "torchvision>=0.16.1",
"--force-reinstall", "--force-reinstall",
"--find-links" if find_links is not None else None, "--find-links" if find_links is not None else None,
find_links, find_links,

View File

@ -45,6 +45,9 @@ async def list_model_records(
base_models: Optional[List[BaseModelType]] = Query(default=None, description="Base models to include"), base_models: Optional[List[BaseModelType]] = Query(default=None, description="Base models to include"),
model_type: Optional[ModelType] = Query(default=None, description="The type of model to get"), model_type: Optional[ModelType] = Query(default=None, description="The type of model to get"),
model_name: Optional[str] = Query(default=None, description="Exact match on the name of the model"), model_name: Optional[str] = Query(default=None, description="Exact match on the name of the model"),
model_format: Optional[str] = Query(
default=None, description="Exact match on the format of the model (e.g. 'diffusers')"
),
) -> ModelsList: ) -> ModelsList:
"""Get a list of models.""" """Get a list of models."""
record_store = ApiDependencies.invoker.services.model_records record_store = ApiDependencies.invoker.services.model_records
@ -52,10 +55,14 @@ async def list_model_records(
if base_models: if base_models:
for base_model in base_models: for base_model in base_models:
found_models.extend( found_models.extend(
record_store.search_by_attr(base_model=base_model, model_type=model_type, model_name=model_name) record_store.search_by_attr(
base_model=base_model, model_type=model_type, model_name=model_name, model_format=model_format
)
) )
else: else:
found_models.extend(record_store.search_by_attr(model_type=model_type, model_name=model_name)) found_models.extend(
record_store.search_by_attr(model_type=model_type, model_name=model_name, model_format=model_format)
)
return ModelsList(models=found_models) return ModelsList(models=found_models)

View File

@ -13,7 +13,15 @@ from invokeai.app.shared.fields import FieldDescriptions
from invokeai.backend.image_util.invisible_watermark import InvisibleWatermark from invokeai.backend.image_util.invisible_watermark import InvisibleWatermark
from invokeai.backend.image_util.safety_checker import SafetyChecker from invokeai.backend.image_util.safety_checker import SafetyChecker
from .baseinvocation import BaseInvocation, Input, InputField, InvocationContext, WithMetadata, invocation from .baseinvocation import (
BaseInvocation,
Classification,
Input,
InputField,
InvocationContext,
WithMetadata,
invocation,
)
@invocation("show_image", title="Show Image", tags=["image"], category="image", version="1.0.0") @invocation("show_image", title="Show Image", tags=["image"], category="image", version="1.0.0")
@ -421,6 +429,64 @@ class ImageBlurInvocation(BaseInvocation, WithMetadata):
) )
@invocation(
"unsharp_mask",
title="Unsharp Mask",
tags=["image", "unsharp_mask"],
category="image",
version="1.2.0",
classification=Classification.Beta,
)
class UnsharpMaskInvocation(BaseInvocation, WithMetadata):
"""Applies an unsharp mask filter to an image"""
image: ImageField = InputField(description="The image to use")
radius: float = InputField(gt=0, description="Unsharp mask radius", default=2)
strength: float = InputField(ge=0, description="Unsharp mask strength", default=50)
def pil_from_array(self, arr):
return Image.fromarray((arr * 255).astype("uint8"))
def array_from_pil(self, img):
return numpy.array(img) / 255
def invoke(self, context: InvocationContext) -> ImageOutput:
image = context.services.images.get_pil_image(self.image.image_name)
mode = image.mode
alpha_channel = image.getchannel("A") if mode == "RGBA" else None
image = image.convert("RGB")
image_blurred = self.array_from_pil(image.filter(ImageFilter.GaussianBlur(radius=self.radius)))
image = self.array_from_pil(image)
image += (image - image_blurred) * (self.strength / 100.0)
image = numpy.clip(image, 0, 1)
image = self.pil_from_array(image)
image = image.convert(mode)
# Make the image RGBA if we had a source alpha channel
if alpha_channel is not None:
image.putalpha(alpha_channel)
image_dto = context.services.images.create(
image=image,
image_origin=ResourceOrigin.INTERNAL,
image_category=ImageCategory.GENERAL,
node_id=self.id,
session_id=context.graph_execution_state_id,
is_intermediate=self.is_intermediate,
metadata=self.metadata,
workflow=context.workflow,
)
return ImageOutput(
image=ImageField(image_name=image_dto.image_name),
width=image.width,
height=image.height,
)
PIL_RESAMPLING_MODES = Literal[ PIL_RESAMPLING_MODES = Literal[
"nearest", "nearest",
"box", "box",

View File

@ -38,7 +38,14 @@ class CalculateImageTilesOutput(BaseInvocationOutput):
tiles: list[Tile] = OutputField(description="The tiles coordinates that cover a particular image shape.") tiles: list[Tile] = OutputField(description="The tiles coordinates that cover a particular image shape.")
@invocation("calculate_image_tiles", title="Calculate Image Tiles", tags=["tiles"], category="tiles", version="1.0.0") @invocation(
"calculate_image_tiles",
title="Calculate Image Tiles",
tags=["tiles"],
category="tiles",
version="1.0.0",
classification=Classification.Beta,
)
class CalculateImageTilesInvocation(BaseInvocation): class CalculateImageTilesInvocation(BaseInvocation):
"""Calculate the coordinates and overlaps of tiles that cover a target image shape.""" """Calculate the coordinates and overlaps of tiles that cover a target image shape."""

View File

@ -7,7 +7,7 @@ from abc import ABC, abstractmethod
from pathlib import Path from pathlib import Path
from typing import List, Optional, Union from typing import List, Optional, Union
from invokeai.backend.model_manager.config import AnyModelConfig, BaseModelType, ModelType from invokeai.backend.model_manager.config import AnyModelConfig, BaseModelType, ModelFormat, ModelType
class DuplicateModelException(Exception): class DuplicateModelException(Exception):
@ -106,6 +106,7 @@ class ModelRecordServiceBase(ABC):
model_name: Optional[str] = None, model_name: Optional[str] = None,
base_model: Optional[BaseModelType] = None, base_model: Optional[BaseModelType] = None,
model_type: Optional[ModelType] = None, model_type: Optional[ModelType] = None,
model_format: Optional[ModelFormat] = None,
) -> List[AnyModelConfig]: ) -> List[AnyModelConfig]:
""" """
Return models matching name, base and/or type. Return models matching name, base and/or type.
@ -113,6 +114,7 @@ class ModelRecordServiceBase(ABC):
:param model_name: Filter by name of model (optional) :param model_name: Filter by name of model (optional)
:param base_model: Filter by base model (optional) :param base_model: Filter by base model (optional)
:param model_type: Filter by type of model (optional) :param model_type: Filter by type of model (optional)
:param model_format: Filter by model format (e.g. "diffusers") (optional)
If none of the optional filters are passed, will return all If none of the optional filters are passed, will return all
models in the database. models in the database.

View File

@ -49,6 +49,7 @@ from invokeai.backend.model_manager.config import (
AnyModelConfig, AnyModelConfig,
BaseModelType, BaseModelType,
ModelConfigFactory, ModelConfigFactory,
ModelFormat,
ModelType, ModelType,
) )
@ -225,6 +226,7 @@ class ModelRecordServiceSQL(ModelRecordServiceBase):
model_name: Optional[str] = None, model_name: Optional[str] = None,
base_model: Optional[BaseModelType] = None, base_model: Optional[BaseModelType] = None,
model_type: Optional[ModelType] = None, model_type: Optional[ModelType] = None,
model_format: Optional[ModelFormat] = None,
) -> List[AnyModelConfig]: ) -> List[AnyModelConfig]:
""" """
Return models matching name, base and/or type. Return models matching name, base and/or type.
@ -232,6 +234,7 @@ class ModelRecordServiceSQL(ModelRecordServiceBase):
:param model_name: Filter by name of model (optional) :param model_name: Filter by name of model (optional)
:param base_model: Filter by base model (optional) :param base_model: Filter by base model (optional)
:param model_type: Filter by type of model (optional) :param model_type: Filter by type of model (optional)
:param model_format: Filter by model format (e.g. "diffusers") (optional)
If none of the optional filters are passed, will return all If none of the optional filters are passed, will return all
models in the database. models in the database.
@ -248,6 +251,9 @@ class ModelRecordServiceSQL(ModelRecordServiceBase):
if model_type: if model_type:
where_clause.append("type=?") where_clause.append("type=?")
bindings.append(model_type) bindings.append(model_type)
if model_format:
where_clause.append("format=?")
bindings.append(model_format)
where = f"WHERE {' AND '.join(where_clause)}" if where_clause else "" where = f"WHERE {' AND '.join(where_clause)}" if where_clause else ""
with self._db.lock: with self._db.lock:
self._cursor.execute( self._cursor.execute(

View File

@ -1,8 +1,15 @@
import sqlite3 import sqlite3
from logging import Logger from logging import Logger
from pydantic import ValidationError
from tqdm import tqdm from tqdm import tqdm
from invokeai.app.services.image_files.image_files_base import ImageFileStorageBase from invokeai.app.services.image_files.image_files_base import ImageFileStorageBase
from invokeai.app.services.image_files.image_files_common import ImageFileNotFoundException
from invokeai.app.services.shared.sqlite_migrator.sqlite_migrator_common import Migration from invokeai.app.services.shared.sqlite_migrator.sqlite_migrator_common import Migration
from invokeai.app.services.workflow_records.workflow_records_common import (
UnsafeWorkflowWithVersionValidator,
)
class Migration2Callback: class Migration2Callback:
@ -132,7 +139,7 @@ class Migration2Callback:
This migrate callback checks each image for the presence of an embedded workflow, then updates its entry This migrate callback checks each image for the presence of an embedded workflow, then updates its entry
in the database accordingly. in the database accordingly.
""" """
# Get the total number of images and chunk it into pages # Get all image names
cursor.execute("SELECT image_name FROM images") cursor.execute("SELECT image_name FROM images")
image_names: list[str] = [image[0] for image in cursor.fetchall()] image_names: list[str] = [image[0] for image in cursor.fetchall()]
total_image_names = len(image_names) total_image_names = len(image_names)
@ -149,11 +156,16 @@ class Migration2Callback:
pbar.set_description(f"Checking image {idx + 1}/{total_image_names} for workflow") pbar.set_description(f"Checking image {idx + 1}/{total_image_names} for workflow")
try: try:
pil_image = self._image_files.get(image_name) pil_image = self._image_files.get(image_name)
if "invokeai_workflow" in pil_image.info: except ImageFileNotFoundException:
to_migrate.append((True, image_name)) self._logger.warning(f"Image {image_name} not found, skipping")
except KeyError: continue
self._logger.warning(f"Image '{image_name}' not found. Skipping.") if "invokeai_workflow" in pil_image.info:
continue try:
UnsafeWorkflowWithVersionValidator.validate_json(pil_image.info.get("invokeai_workflow", ""))
except ValidationError:
self._logger.warning(f"Image {image_name} has invalid embedded workflow, skipping")
continue
to_migrate.append((True, image_name))
self._logger.info(f"Adding {len(to_migrate)} embedded workflows to database") self._logger.info(f"Adding {len(to_migrate)} embedded workflows to database")
cursor.executemany("UPDATE images SET has_workflow = ? WHERE image_name = ?", to_migrate) cursor.executemany("UPDATE images SET has_workflow = ? WHERE image_name = ?", to_migrate)

View File

@ -65,12 +65,24 @@ class WorkflowWithoutID(BaseModel):
nodes: list[dict[str, JsonValue]] = Field(description="The nodes of the workflow.") nodes: list[dict[str, JsonValue]] = Field(description="The nodes of the workflow.")
edges: list[dict[str, JsonValue]] = Field(description="The edges of the workflow.") edges: list[dict[str, JsonValue]] = Field(description="The edges of the workflow.")
model_config = ConfigDict(extra="forbid") model_config = ConfigDict(extra="ignore")
WorkflowWithoutIDValidator = TypeAdapter(WorkflowWithoutID) WorkflowWithoutIDValidator = TypeAdapter(WorkflowWithoutID)
class UnsafeWorkflowWithVersion(BaseModel):
"""
This utility model only requires a workflow to have a valid version string.
It is used to validate a workflow version without having to validate the entire workflow.
"""
meta: WorkflowMeta = Field(description="The meta of the workflow.")
UnsafeWorkflowWithVersionValidator = TypeAdapter(UnsafeWorkflowWithVersion)
class Workflow(WorkflowWithoutID): class Workflow(WorkflowWithoutID):
id: str = Field(description="The id of the workflow.") id: str = Field(description="The id of the workflow.")

View File

@ -950,9 +950,9 @@
"problemSettingTitle": "Problem Setting Title", "problemSettingTitle": "Problem Setting Title",
"reloadNodeTemplates": "Reload Node Templates", "reloadNodeTemplates": "Reload Node Templates",
"removeLinearView": "Remove from Linear View", "removeLinearView": "Remove from Linear View",
"resetWorkflow": "Reset Workflow Editor", "newWorkflow": "New Workflow",
"resetWorkflowDesc": "Are you sure you want to reset the Workflow Editor?", "newWorkflowDesc": "Create a new workflow?",
"resetWorkflowDesc2": "Resetting the Workflow Editor will clear all nodes, edges and workflow details. Saved workflows will not be affected.", "newWorkflowDesc2": "Your current workflow has unsaved changes.",
"scheduler": "Scheduler", "scheduler": "Scheduler",
"schedulerDescription": "TODO", "schedulerDescription": "TODO",
"sDXLMainModelField": "SDXL Model", "sDXLMainModelField": "SDXL Model",
@ -1634,10 +1634,10 @@
"userWorkflows": "My Workflows", "userWorkflows": "My Workflows",
"defaultWorkflows": "Default Workflows", "defaultWorkflows": "Default Workflows",
"openWorkflow": "Open Workflow", "openWorkflow": "Open Workflow",
"uploadWorkflow": "Upload Workflow", "uploadWorkflow": "Load from File",
"deleteWorkflow": "Delete Workflow", "deleteWorkflow": "Delete Workflow",
"unnamedWorkflow": "Unnamed Workflow", "unnamedWorkflow": "Unnamed Workflow",
"downloadWorkflow": "Download Workflow", "downloadWorkflow": "Save to File",
"saveWorkflow": "Save Workflow", "saveWorkflow": "Save Workflow",
"saveWorkflowAs": "Save Workflow As", "saveWorkflowAs": "Save Workflow As",
"savingWorkflow": "Saving Workflow...", "savingWorkflow": "Saving Workflow...",
@ -1652,7 +1652,7 @@
"searchWorkflows": "Search Workflows", "searchWorkflows": "Search Workflows",
"clearWorkflowSearchFilter": "Clear Workflow Search Filter", "clearWorkflowSearchFilter": "Clear Workflow Search Filter",
"workflowName": "Workflow Name", "workflowName": "Workflow Name",
"workflowEditorReset": "Workflow Editor Reset", "newWorkflowCreated": "New Workflow Created",
"workflowEditorMenu": "Workflow Editor Menu", "workflowEditorMenu": "Workflow Editor Menu",
"workflowIsOpen": "Workflow is Open" "workflowIsOpen": "Workflow is Open"
}, },

View File

@ -104,7 +104,16 @@
"copyError": "$t(gallery.copy) Errore", "copyError": "$t(gallery.copy) Errore",
"input": "Ingresso", "input": "Ingresso",
"notInstalled": "Non $t(common.installed)", "notInstalled": "Non $t(common.installed)",
"unknownError": "Errore sconosciuto" "unknownError": "Errore sconosciuto",
"updated": "Aggiornato",
"save": "Salva",
"created": "Creato",
"prevPage": "Pagina precedente",
"delete": "Elimina",
"orderBy": "Ordinato per",
"nextPage": "Pagina successiva",
"saveAs": "Salva come",
"unsaved": "Non salvato"
}, },
"gallery": { "gallery": {
"generations": "Generazioni", "generations": "Generazioni",
@ -763,7 +772,10 @@
"setIPAdapterImage": "Imposta come immagine per l'Adattatore IP", "setIPAdapterImage": "Imposta come immagine per l'Adattatore IP",
"problemSavingMaskDesc": "Impossibile salvare la maschera", "problemSavingMaskDesc": "Impossibile salvare la maschera",
"setAsCanvasInitialImage": "Imposta come immagine iniziale della tela", "setAsCanvasInitialImage": "Imposta come immagine iniziale della tela",
"invalidUpload": "Caricamento non valido" "invalidUpload": "Caricamento non valido",
"problemDeletingWorkflow": "Problema durante l'eliminazione del flusso di lavoro",
"workflowDeleted": "Flusso di lavoro eliminato",
"problemRetrievingWorkflow": "Problema nel recupero del flusso di lavoro"
}, },
"tooltip": { "tooltip": {
"feature": { "feature": {
@ -886,11 +898,11 @@
"zoomInNodes": "Ingrandire", "zoomInNodes": "Ingrandire",
"fitViewportNodes": "Adatta vista", "fitViewportNodes": "Adatta vista",
"showGraphNodes": "Mostra sovrapposizione grafico", "showGraphNodes": "Mostra sovrapposizione grafico",
"resetWorkflowDesc2": "Reimpostare il flusso di lavoro cancellerà tutti i nodi, i bordi e i dettagli del flusso di lavoro.", "resetWorkflowDesc2": "Il ripristino dell'editor del flusso di lavoro cancellerà tutti i nodi, le connessioni e i dettagli del flusso di lavoro. I flussi di lavoro salvati non saranno interessati.",
"reloadNodeTemplates": "Ricarica i modelli di nodo", "reloadNodeTemplates": "Ricarica i modelli di nodo",
"loadWorkflow": "Importa flusso di lavoro JSON", "loadWorkflow": "Importa flusso di lavoro JSON",
"resetWorkflow": "Reimposta flusso di lavoro", "resetWorkflow": "Reimposta l'editor del flusso di lavoro",
"resetWorkflowDesc": "Sei sicuro di voler reimpostare questo flusso di lavoro?", "resetWorkflowDesc": "Sei sicuro di voler reimpostare l'editor del flusso di lavoro?",
"downloadWorkflow": "Esporta flusso di lavoro JSON", "downloadWorkflow": "Esporta flusso di lavoro JSON",
"scheduler": "Campionatore", "scheduler": "Campionatore",
"addNode": "Aggiungi nodo", "addNode": "Aggiungi nodo",
@ -1080,25 +1092,27 @@
"collectionOrScalarFieldType": "{{name}} Raccolta|Scalare", "collectionOrScalarFieldType": "{{name}} Raccolta|Scalare",
"nodeVersion": "Versione Nodo", "nodeVersion": "Versione Nodo",
"inputFieldTypeParseError": "Impossibile analizzare il tipo di campo di input {{node}}.{{field}} ({{message}})", "inputFieldTypeParseError": "Impossibile analizzare il tipo di campo di input {{node}}.{{field}} ({{message}})",
"unsupportedArrayItemType": "tipo di elemento dell'array non supportato \"{{type}}\"", "unsupportedArrayItemType": "Tipo di elemento dell'array non supportato \"{{type}}\"",
"targetNodeFieldDoesNotExist": "Connessione non valida: il campo di destinazione/input {{node}}.{{field}} non esiste", "targetNodeFieldDoesNotExist": "Connessione non valida: il campo di destinazione/input {{node}}.{{field}} non esiste",
"unsupportedMismatchedUnion": "tipo CollectionOrScalar non corrispondente con tipi di base {{firstType}} e {{secondType}}", "unsupportedMismatchedUnion": "tipo CollectionOrScalar non corrispondente con tipi di base {{firstType}} e {{secondType}}",
"allNodesUpdated": "Tutti i nodi sono aggiornati", "allNodesUpdated": "Tutti i nodi sono aggiornati",
"sourceNodeDoesNotExist": "Connessione non valida: il nodo di origine/output {{node}} non esiste", "sourceNodeDoesNotExist": "Connessione non valida: il nodo di origine/output {{node}} non esiste",
"unableToExtractEnumOptions": "impossibile estrarre le opzioni enum", "unableToExtractEnumOptions": "Impossibile estrarre le opzioni enum",
"unableToParseFieldType": "impossibile analizzare il tipo di campo", "unableToParseFieldType": "Impossibile analizzare il tipo di campo",
"unrecognizedWorkflowVersion": "Versione dello schema del flusso di lavoro non riconosciuta {{version}}", "unrecognizedWorkflowVersion": "Versione dello schema del flusso di lavoro non riconosciuta {{version}}",
"outputFieldTypeParseError": "Impossibile analizzare il tipo di campo di output {{node}}.{{field}} ({{message}})", "outputFieldTypeParseError": "Impossibile analizzare il tipo di campo di output {{node}}.{{field}} ({{message}})",
"sourceNodeFieldDoesNotExist": "Connessione non valida: il campo di origine/output {{node}}.{{field}} non esiste", "sourceNodeFieldDoesNotExist": "Connessione non valida: il campo di origine/output {{node}}.{{field}} non esiste",
"unableToGetWorkflowVersion": "Impossibile ottenere la versione dello schema del flusso di lavoro", "unableToGetWorkflowVersion": "Impossibile ottenere la versione dello schema del flusso di lavoro",
"nodePack": "Pacchetto di nodi", "nodePack": "Pacchetto di nodi",
"unableToExtractSchemaNameFromRef": "impossibile estrarre il nome dello schema dal riferimento", "unableToExtractSchemaNameFromRef": "Impossibile estrarre il nome dello schema dal riferimento",
"unknownOutput": "Output sconosciuto: {{name}}", "unknownOutput": "Output sconosciuto: {{name}}",
"unknownNodeType": "Tipo di nodo sconosciuto", "unknownNodeType": "Tipo di nodo sconosciuto",
"targetNodeDoesNotExist": "Connessione non valida: il nodo di destinazione/input {{node}} non esiste", "targetNodeDoesNotExist": "Connessione non valida: il nodo di destinazione/input {{node}} non esiste",
"unknownFieldType": "$t(nodes.unknownField) tipo: {{type}}", "unknownFieldType": "$t(nodes.unknownField) tipo: {{type}}",
"deletedInvalidEdge": "Eliminata connessione non valida {{source}} -> {{target}}", "deletedInvalidEdge": "Eliminata connessione non valida {{source}} -> {{target}}",
"unknownInput": "Input sconosciuto: {{name}}" "unknownInput": "Input sconosciuto: {{name}}",
"prototypeDesc": "Questa invocazione è un prototipo. Potrebbe subire modifiche sostanziali durante gli aggiornamenti dell'app e potrebbe essere rimossa in qualsiasi momento.",
"betaDesc": "Questa invocazione è in versione beta. Fino a quando non sarà stabile, potrebbe subire modifiche importanti durante gli aggiornamenti dell'app. Abbiamo intenzione di supportare questa invocazione a lungo termine."
}, },
"boards": { "boards": {
"autoAddBoard": "Aggiungi automaticamente bacheca", "autoAddBoard": "Aggiungi automaticamente bacheca",
@ -1594,5 +1608,34 @@
"hrf": "Correzione Alta Risoluzione", "hrf": "Correzione Alta Risoluzione",
"hrfStrength": "Forza della Correzione Alta Risoluzione", "hrfStrength": "Forza della Correzione Alta Risoluzione",
"strengthTooltip": "Valori più bassi comportano meno dettagli, il che può ridurre potenziali artefatti." "strengthTooltip": "Valori più bassi comportano meno dettagli, il che può ridurre potenziali artefatti."
},
"workflows": {
"saveWorkflowAs": "Salva flusso di lavoro come",
"workflowEditorMenu": "Menu dell'editor del flusso di lavoro",
"noSystemWorkflows": "Nessun flusso di lavoro del sistema",
"workflowName": "Nome del flusso di lavoro",
"noUserWorkflows": "Nessun flusso di lavoro utente",
"defaultWorkflows": "Flussi di lavoro predefiniti",
"saveWorkflow": "Salva flusso di lavoro",
"openWorkflow": "Apri flusso di lavoro",
"clearWorkflowSearchFilter": "Cancella il filtro di ricerca del flusso di lavoro",
"workflowEditorReset": "Reimpostazione dell'editor del flusso di lavoro",
"workflowLibrary": "Libreria",
"noRecentWorkflows": "Nessun flusso di lavoro recente",
"workflowSaved": "Flusso di lavoro salvato",
"workflowIsOpen": "Il flusso di lavoro è aperto",
"unnamedWorkflow": "Flusso di lavoro senza nome",
"savingWorkflow": "Salvataggio del flusso di lavoro...",
"problemLoading": "Problema durante il caricamento dei flussi di lavoro",
"loading": "Caricamento dei flussi di lavoro",
"searchWorkflows": "Cerca flussi di lavoro",
"problemSavingWorkflow": "Problema durante il salvataggio del flusso di lavoro",
"deleteWorkflow": "Elimina flusso di lavoro",
"workflows": "Flussi di lavoro",
"noDescription": "Nessuna descrizione",
"userWorkflows": "I miei flussi di lavoro"
},
"app": {
"storeNotInitialized": "Il negozio non è inizializzato"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -110,7 +110,17 @@
"copyError": "$t(gallery.copy) 错误", "copyError": "$t(gallery.copy) 错误",
"input": "输入", "input": "输入",
"notInstalled": "非 $t(common.installed)", "notInstalled": "非 $t(common.installed)",
"delete": "删除" "delete": "删除",
"updated": "已上传",
"save": "保存",
"created": "已创建",
"prevPage": "上一页",
"unknownError": "未知错误",
"direction": "指向",
"orderBy": "排序方式:",
"nextPage": "下一页",
"saveAs": "保存为",
"unsaved": "未保存"
}, },
"gallery": { "gallery": {
"generations": "生成的图像", "generations": "生成的图像",
@ -146,7 +156,11 @@
"image": "图像", "image": "图像",
"drop": "弃用", "drop": "弃用",
"dropOrUpload": "$t(gallery.drop) 或上传", "dropOrUpload": "$t(gallery.drop) 或上传",
"dropToUpload": "$t(gallery.drop) 以上传" "dropToUpload": "$t(gallery.drop) 以上传",
"problemDeletingImagesDesc": "有一张或多张图像无法被删除",
"problemDeletingImages": "删除图像时出现问题",
"unstarImage": "取消收藏图像",
"starImage": "收藏图像"
}, },
"hotkeys": { "hotkeys": {
"keyboardShortcuts": "键盘快捷键", "keyboardShortcuts": "键盘快捷键",
@ -724,7 +738,7 @@
"nodesUnrecognizedTypes": "无法加载。节点图有无法识别的节点类型", "nodesUnrecognizedTypes": "无法加载。节点图有无法识别的节点类型",
"nodesNotValidJSON": "无效的 JSON", "nodesNotValidJSON": "无效的 JSON",
"nodesNotValidGraph": "无效的 InvokeAi 节点图", "nodesNotValidGraph": "无效的 InvokeAi 节点图",
"nodesLoadedFailed": "节点加载失败", "nodesLoadedFailed": "节点加载失败",
"modelAddedSimple": "已添加模型", "modelAddedSimple": "已添加模型",
"modelAdded": "已添加模型: {{modelName}}", "modelAdded": "已添加模型: {{modelName}}",
"imageSavingFailed": "图像保存失败", "imageSavingFailed": "图像保存失败",
@ -760,7 +774,10 @@
"problemImportingMask": "导入遮罩时出现问题", "problemImportingMask": "导入遮罩时出现问题",
"baseModelChangedCleared_other": "基础模型已更改, 已清除或禁用 {{count}} 个不兼容的子模型", "baseModelChangedCleared_other": "基础模型已更改, 已清除或禁用 {{count}} 个不兼容的子模型",
"setAsCanvasInitialImage": "设为画布初始图像", "setAsCanvasInitialImage": "设为画布初始图像",
"invalidUpload": "无效的上传" "invalidUpload": "无效的上传",
"problemDeletingWorkflow": "删除工作流时出现问题",
"workflowDeleted": "已删除工作流",
"problemRetrievingWorkflow": "检索工作流时发生问题"
}, },
"unifiedCanvas": { "unifiedCanvas": {
"layer": "图层", "layer": "图层",
@ -875,11 +892,11 @@
}, },
"nodes": { "nodes": {
"zoomInNodes": "放大", "zoomInNodes": "放大",
"resetWorkflowDesc": "是否确定要清空节点图?", "resetWorkflowDesc": "是否确定要重置工作流编辑器?",
"resetWorkflow": "清空节点图", "resetWorkflow": "重置工作流编辑器",
"loadWorkflow": "读取节点图", "loadWorkflow": "加载工作流",
"zoomOutNodes": "缩小", "zoomOutNodes": "缩小",
"resetWorkflowDesc2": "重置节点图将清除所有节点、边际和节点图详情.", "resetWorkflowDesc2": "重置工作流编辑器将清除所有节点、边际和节点图详情。不影响已保存的工作流。",
"reloadNodeTemplates": "重载节点模板", "reloadNodeTemplates": "重载节点模板",
"hideGraphNodes": "隐藏节点图信息", "hideGraphNodes": "隐藏节点图信息",
"fitViewportNodes": "自适应视图", "fitViewportNodes": "自适应视图",
@ -888,7 +905,7 @@
"showLegendNodes": "显示字段类型图例", "showLegendNodes": "显示字段类型图例",
"hideLegendNodes": "隐藏字段类型图例", "hideLegendNodes": "隐藏字段类型图例",
"showGraphNodes": "显示节点图信息", "showGraphNodes": "显示节点图信息",
"downloadWorkflow": "下载节点图 JSON", "downloadWorkflow": "下载工作流 JSON",
"workflowDescription": "简述", "workflowDescription": "简述",
"versionUnknown": " 未知版本", "versionUnknown": " 未知版本",
"noNodeSelected": "无选中的节点", "noNodeSelected": "无选中的节点",
@ -1103,7 +1120,9 @@
"collectionOrScalarFieldType": "{{name}} 合集 | 标量", "collectionOrScalarFieldType": "{{name}} 合集 | 标量",
"nodeVersion": "节点版本", "nodeVersion": "节点版本",
"deletedInvalidEdge": "已删除无效的边缘 {{source}} -> {{target}}", "deletedInvalidEdge": "已删除无效的边缘 {{source}} -> {{target}}",
"unknownInput": "未知输入:{{name}}" "unknownInput": "未知输入:{{name}}",
"prototypeDesc": "此调用是一个原型 (prototype)。它可能会在本项目更新期间发生破坏性更改,并且随时可能被删除。",
"betaDesc": "此调用尚处于测试阶段。在稳定之前,它可能会在项目更新期间发生破坏性更改。本项目计划长期支持这种调用。"
}, },
"controlnet": { "controlnet": {
"resize": "直接缩放", "resize": "直接缩放",
@ -1607,5 +1626,36 @@
"hrf": "高分辨率修复", "hrf": "高分辨率修复",
"hrfStrength": "高分辨率修复强度", "hrfStrength": "高分辨率修复强度",
"strengthTooltip": "值越低细节越少,但可以减少部分潜在的伪影。" "strengthTooltip": "值越低细节越少,但可以减少部分潜在的伪影。"
},
"workflows": {
"saveWorkflowAs": "保存工作流为",
"workflowEditorMenu": "工作流编辑器菜单",
"noSystemWorkflows": "无系统工作流",
"workflowName": "工作流名称",
"noUserWorkflows": "无用户工作流",
"defaultWorkflows": "默认工作流",
"saveWorkflow": "保存工作流",
"openWorkflow": "打开工作流",
"clearWorkflowSearchFilter": "清除工作流检索过滤器",
"workflowEditorReset": "工作流编辑器重置",
"workflowLibrary": "工作流库",
"downloadWorkflow": "下载工作流",
"noRecentWorkflows": "无最近工作流",
"workflowSaved": "已保存工作流",
"workflowIsOpen": "工作流已打开",
"unnamedWorkflow": "未命名的工作流",
"savingWorkflow": "保存工作流中...",
"problemLoading": "加载工作流时出现问题",
"loading": "加载工作流中",
"searchWorkflows": "检索工作流",
"problemSavingWorkflow": "保存工作流时出现问题",
"deleteWorkflow": "删除工作流",
"workflows": "工作流",
"noDescription": "无描述",
"uploadWorkflow": "上传工作流",
"userWorkflows": "我的工作流"
},
"app": {
"storeNotInitialized": "商店尚未初始化"
} }
} }

View File

@ -64,7 +64,10 @@ const migrateV1toV2 = (workflowToMigrate: WorkflowV1): WorkflowV2 => {
const nodePack = invocationTemplate const nodePack = invocationTemplate
? invocationTemplate.nodePack ? invocationTemplate.nodePack
: t('common.unknown'); : t('common.unknown');
(node.data as unknown as InvocationNodeData).nodePack = nodePack; (node.data as unknown as InvocationNodeData).nodePack = nodePack;
// Fallback to 1.0.0 if not specified - this matches the behavior of the backend
node.data.version ||= '1.0.0';
} }
}); });
// Bump version // Bump version

View File

@ -11,44 +11,48 @@ import {
Text, Text,
useDisclosure, useDisclosure,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useAppDispatch } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { nodeEditorReset } from 'features/nodes/store/nodesSlice'; import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast'; import { makeToast } from 'features/system/util/makeToast';
import { memo, useCallback, useRef } from 'react'; import { memo, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FaTrash } from 'react-icons/fa'; import { FaCircleNodes } from 'react-icons/fa6';
const ResetWorkflowEditorMenuItem = () => { const NewWorkflowMenuItem = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const cancelRef = useRef<HTMLButtonElement | null>(null); const cancelRef = useRef<HTMLButtonElement | null>(null);
const isTouched = useAppSelector((state) => state.workflow.isTouched);
const handleConfirmClear = useCallback(() => { const handleNewWorkflow = useCallback(() => {
dispatch(nodeEditorReset()); dispatch(nodeEditorReset());
dispatch( dispatch(
addToast( addToast(
makeToast({ makeToast({
title: t('workflows.workflowEditorReset'), title: t('workflows.newWorkflowCreated'),
status: 'success', status: 'success',
}) })
) )
); );
onClose(); onClose();
}, [dispatch, t, onClose]); }, [dispatch, onClose, t]);
const onClick = useCallback(() => {
if (!isTouched) {
handleNewWorkflow();
return;
}
onOpen();
}, [handleNewWorkflow, isTouched, onOpen]);
return ( return (
<> <>
<MenuItem <MenuItem as="button" icon={<FaCircleNodes />} onClick={onClick}>
as="button" {t('nodes.newWorkflow')}
icon={<FaTrash />}
sx={{ color: 'error.600', _dark: { color: 'error.300' } }}
onClick={onOpen}
>
{t('nodes.resetWorkflow')}
</MenuItem> </MenuItem>
<AlertDialog <AlertDialog
@ -61,13 +65,13 @@ const ResetWorkflowEditorMenuItem = () => {
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold"> <AlertDialogHeader fontSize="lg" fontWeight="bold">
{t('nodes.resetWorkflow')} {t('nodes.newWorkflow')}
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogBody py={4}> <AlertDialogBody py={4}>
<Flex flexDir="column" gap={2}> <Flex flexDir="column" gap={2}>
<Text>{t('nodes.resetWorkflowDesc')}</Text> <Text>{t('nodes.newWorkflowDesc')}</Text>
<Text variant="subtext">{t('nodes.resetWorkflowDesc2')}</Text> <Text variant="subtext">{t('nodes.newWorkflowDesc2')}</Text>
</Flex> </Flex>
</AlertDialogBody> </AlertDialogBody>
@ -75,7 +79,7 @@ const ResetWorkflowEditorMenuItem = () => {
<Button ref={cancelRef} onClick={onClose}> <Button ref={cancelRef} onClick={onClose}>
{t('common.cancel')} {t('common.cancel')}
</Button> </Button>
<Button colorScheme="error" ml={3} onClick={handleConfirmClear}> <Button colorScheme="error" ml={3} onClick={handleNewWorkflow}>
{t('common.accept')} {t('common.accept')}
</Button> </Button>
</AlertDialogFooter> </AlertDialogFooter>
@ -85,4 +89,4 @@ const ResetWorkflowEditorMenuItem = () => {
); );
}; };
export default memo(ResetWorkflowEditorMenuItem); export default memo(NewWorkflowMenuItem);

View File

@ -9,7 +9,7 @@ import IAIIconButton from 'common/components/IAIIconButton';
import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger'; import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import DownloadWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/DownloadWorkflowMenuItem'; import DownloadWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/DownloadWorkflowMenuItem';
import ResetWorkflowEditorMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/ResetWorkflowEditorMenuItem'; import NewWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/NewWorkflowMenuItem';
import SaveWorkflowAsMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem'; import SaveWorkflowAsMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowAsMenuItem';
import SaveWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowMenuItem'; import SaveWorkflowMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/SaveWorkflowMenuItem';
import SettingsMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/SettingsMenuItem'; import SettingsMenuItem from 'features/workflowLibrary/components/WorkflowLibraryMenu/SettingsMenuItem';
@ -39,7 +39,7 @@ const WorkflowLibraryMenu = () => {
{isWorkflowLibraryEnabled && <SaveWorkflowAsMenuItem />} {isWorkflowLibraryEnabled && <SaveWorkflowAsMenuItem />}
<DownloadWorkflowMenuItem /> <DownloadWorkflowMenuItem />
<UploadWorkflowMenuItem /> <UploadWorkflowMenuItem />
<ResetWorkflowEditorMenuItem /> <NewWorkflowMenuItem />
<MenuDivider /> <MenuDivider />
<SettingsMenuItem /> <SettingsMenuItem />
</MenuList> </MenuList>

View File

@ -101,6 +101,8 @@ plugins:
extra_javascript: extra_javascript:
- https://unpkg.com/tablesort@5.3.0/dist/tablesort.min.js - https://unpkg.com/tablesort@5.3.0/dist/tablesort.min.js
- javascripts/tablesort.js - javascripts/tablesort.js
- https://widget.kapa.ai/kapa-widget.bundle.js
- javascript/init_kapa_widget.js
extra: extra:
analytics: analytics:

View File

@ -32,7 +32,7 @@ classifiers = [
'Topic :: Scientific/Engineering :: Image Processing', 'Topic :: Scientific/Engineering :: Image Processing',
] ]
dependencies = [ dependencies = [
"accelerate~=0.24.0", "accelerate~=0.25.0",
"albumentations", "albumentations",
"basicsr", "basicsr",
"click", "click",
@ -80,11 +80,11 @@ dependencies = [
"semver~=3.0.1", "semver~=3.0.1",
"send2trash", "send2trash",
"test-tube~=0.7.5", "test-tube~=0.7.5",
"torch==2.1.0", "torch==2.1.1",
"torchvision==0.16.0", "torchvision==0.16.1",
"torchmetrics~=0.11.0", "torchmetrics~=0.11.0",
"torchsde~=0.2.5", "torchsde~=0.2.5",
"transformers~=4.35.0", "transformers~=4.36.0",
"uvicorn[standard]~=0.21.1", "uvicorn[standard]~=0.21.1",
"windows-curses; sys_platform=='win32'", "windows-curses; sys_platform=='win32'",
] ]
@ -107,7 +107,7 @@ dependencies = [
"pytest-datadir", "pytest-datadir",
] ]
"xformers" = [ "xformers" = [
"xformers==0.0.22post7; sys_platform!='darwin'", "xformers==0.0.23; sys_platform!='darwin'",
"triton; sys_platform=='linux'", "triton; sys_platform=='linux'",
] ]
"onnx" = ["onnxruntime"] "onnx" = ["onnxruntime"]