mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
style preset images
This commit is contained in:
parent
2604fd9fde
commit
cc96dcf0ed
@ -31,6 +31,8 @@ from invokeai.app.services.session_processor.session_processor_default import (
|
||||
)
|
||||
from invokeai.app.services.session_queue.session_queue_sqlite import SqliteSessionQueue
|
||||
from invokeai.app.services.shared.sqlite.sqlite_util import init_db
|
||||
from invokeai.app.services.style_preset_images.style_preset_images_base import StylePresetImageFileStorageBase
|
||||
from invokeai.app.services.style_preset_images.style_preset_images_default import StylePresetImageFileStorageDisk
|
||||
from invokeai.app.services.style_preset_records.style_preset_records_sqlite import SqliteStylePresetRecordsStorage
|
||||
from invokeai.app.services.urls.urls_default import LocalUrlService
|
||||
from invokeai.app.services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
|
||||
@ -75,6 +77,7 @@ class ApiDependencies:
|
||||
image_files = DiskImageFileStorage(f"{output_folder}/images")
|
||||
|
||||
model_images_folder = config.models_path
|
||||
style_preset_images_folder = config.style_preset_images_path
|
||||
|
||||
db = init_db(config=config, logger=logger, image_files=image_files)
|
||||
|
||||
@ -111,6 +114,9 @@ class ApiDependencies:
|
||||
urls = LocalUrlService()
|
||||
workflow_records = SqliteWorkflowRecordsStorage(db=db)
|
||||
style_preset_records = SqliteStylePresetRecordsStorage(db=db)
|
||||
style_preset_images_service = StylePresetImageFileStorageDisk(
|
||||
style_preset_images_folder / "style_preset_images"
|
||||
)
|
||||
|
||||
services = InvocationServices(
|
||||
board_image_records=board_image_records,
|
||||
@ -137,6 +143,7 @@ class ApiDependencies:
|
||||
tensors=tensors,
|
||||
conditioning=conditioning,
|
||||
style_preset_records=style_preset_records,
|
||||
style_preset_images_service=style_preset_images_service,
|
||||
)
|
||||
|
||||
ApiDependencies.invoker = Invoker(services)
|
||||
|
@ -1,10 +1,18 @@
|
||||
from fastapi import APIRouter, Body, HTTPException, Path
|
||||
import io
|
||||
import traceback
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Body, File, Form, HTTPException, Path, UploadFile
|
||||
from fastapi.responses import FileResponse
|
||||
from PIL import Image
|
||||
|
||||
from invokeai.app.api.dependencies import ApiDependencies
|
||||
from invokeai.app.api.routers.model_manager import IMAGE_MAX_AGE
|
||||
from invokeai.app.services.style_preset_records.style_preset_records_common import (
|
||||
PresetData,
|
||||
StylePresetChanges,
|
||||
StylePresetNotFoundError,
|
||||
StylePresetRecordDTO,
|
||||
StylePresetRecordWithImage,
|
||||
StylePresetWithoutId,
|
||||
)
|
||||
|
||||
@ -15,15 +23,17 @@ style_presets_router = APIRouter(prefix="/v1/style_presets", tags=["style_preset
|
||||
"/i/{style_preset_id}",
|
||||
operation_id="get_style_preset",
|
||||
responses={
|
||||
200: {"model": StylePresetRecordDTO},
|
||||
200: {"model": StylePresetRecordWithImage},
|
||||
},
|
||||
)
|
||||
async def get_style_preset(
|
||||
style_preset_id: str = Path(description="The style preset to get"),
|
||||
) -> StylePresetRecordDTO:
|
||||
) -> StylePresetRecordWithImage:
|
||||
"""Gets a style preset"""
|
||||
try:
|
||||
return ApiDependencies.invoker.services.style_preset_records.get(style_preset_id)
|
||||
image = ApiDependencies.invoker.services.style_preset_images_service.get_url(style_preset_id)
|
||||
style_preset = ApiDependencies.invoker.services.style_preset_records.get(style_preset_id)
|
||||
return StylePresetRecordWithImage(image=image, **style_preset.model_dump())
|
||||
except StylePresetNotFoundError:
|
||||
raise HTTPException(status_code=404, detail="Style preset not found")
|
||||
|
||||
@ -32,15 +42,45 @@ async def get_style_preset(
|
||||
"/i/{style_preset_id}",
|
||||
operation_id="update_style_preset",
|
||||
responses={
|
||||
200: {"model": StylePresetRecordDTO},
|
||||
200: {"model": StylePresetRecordWithImage},
|
||||
},
|
||||
)
|
||||
async def update_style_preset(
|
||||
style_preset_id: str = Path(description="The id of the style preset to update"),
|
||||
changes: StylePresetChanges = Body(description="The updated style preset", embed=True),
|
||||
) -> StylePresetRecordDTO:
|
||||
name: str = Form(description="The name of the style preset to create"),
|
||||
positive_prompt: str = Form(description="The positive prompt of the style preset"),
|
||||
negative_prompt: str = Form(description="The negative prompt of the style preset"),
|
||||
image: Optional[UploadFile] = File(description="The image file to upload", default=None),
|
||||
) -> StylePresetRecordWithImage:
|
||||
"""Updates a style preset"""
|
||||
return ApiDependencies.invoker.services.style_preset_records.update(id=style_preset_id, changes=changes)
|
||||
if image is not None:
|
||||
if not image.content_type or not image.content_type.startswith("image"):
|
||||
raise HTTPException(status_code=415, detail="Not an image")
|
||||
|
||||
contents = await image.read()
|
||||
try:
|
||||
pil_image = Image.open(io.BytesIO(contents))
|
||||
|
||||
except Exception:
|
||||
ApiDependencies.invoker.services.logger.error(traceback.format_exc())
|
||||
raise HTTPException(status_code=415, detail="Failed to read image")
|
||||
|
||||
try:
|
||||
ApiDependencies.invoker.services.style_preset_images_service.save(pil_image, style_preset_id)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=409, detail=str(e))
|
||||
else:
|
||||
try:
|
||||
ApiDependencies.invoker.services.style_preset_images_service.delete(style_preset_id)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=409, detail=str(e))
|
||||
|
||||
preset_data = PresetData(positive_prompt=positive_prompt, negative_prompt=negative_prompt)
|
||||
changes = StylePresetChanges(name=name, preset_data=preset_data)
|
||||
|
||||
style_preset_image = ApiDependencies.invoker.services.style_preset_images_service.get_url(style_preset_id)
|
||||
style_preset = ApiDependencies.invoker.services.style_preset_records.update(id=style_preset_id, changes=changes)
|
||||
return StylePresetRecordWithImage(image=style_preset_image, **style_preset.model_dump())
|
||||
|
||||
|
||||
@style_presets_router.delete(
|
||||
@ -51,6 +91,7 @@ async def delete_style_preset(
|
||||
style_preset_id: str = Path(description="The style preset to delete"),
|
||||
) -> None:
|
||||
"""Deletes a style preset"""
|
||||
ApiDependencies.invoker.services.style_preset_images_service.delete(style_preset_id)
|
||||
ApiDependencies.invoker.services.style_preset_records.delete(style_preset_id)
|
||||
|
||||
|
||||
@ -58,23 +99,87 @@ async def delete_style_preset(
|
||||
"/",
|
||||
operation_id="create_style_preset",
|
||||
responses={
|
||||
200: {"model": StylePresetRecordDTO},
|
||||
200: {"model": StylePresetRecordWithImage},
|
||||
},
|
||||
)
|
||||
async def create_style_preset(
|
||||
style_preset: StylePresetWithoutId = Body(description="The style preset to create", embed=True),
|
||||
) -> StylePresetRecordDTO:
|
||||
name: str = Form(description="The name of the style preset to create"),
|
||||
positive_prompt: str = Form(description="The positive prompt of the style preset"),
|
||||
negative_prompt: str = Form(description="The negative prompt of the style preset"),
|
||||
image: Optional[UploadFile] = File(description="The image file to upload", default=None),
|
||||
) -> StylePresetRecordWithImage:
|
||||
"""Creates a style preset"""
|
||||
return ApiDependencies.invoker.services.style_preset_records.create(style_preset=style_preset)
|
||||
preset_data = PresetData(positive_prompt=positive_prompt, negative_prompt=negative_prompt)
|
||||
style_preset = StylePresetWithoutId(name=name, preset_data=preset_data)
|
||||
new_style_preset = ApiDependencies.invoker.services.style_preset_records.create(style_preset=style_preset)
|
||||
|
||||
if image is not None:
|
||||
if not image.content_type or not image.content_type.startswith("image"):
|
||||
raise HTTPException(status_code=415, detail="Not an image")
|
||||
|
||||
contents = await image.read()
|
||||
try:
|
||||
pil_image = Image.open(io.BytesIO(contents))
|
||||
|
||||
except Exception:
|
||||
ApiDependencies.invoker.services.logger.error(traceback.format_exc())
|
||||
raise HTTPException(status_code=415, detail="Failed to read image")
|
||||
|
||||
try:
|
||||
ApiDependencies.invoker.services.style_preset_images_service.save(pil_image, new_style_preset.id)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=409, detail=str(e))
|
||||
|
||||
preset_image = ApiDependencies.invoker.services.style_preset_images_service.get_url(new_style_preset.id)
|
||||
return StylePresetRecordWithImage(image=preset_image, **new_style_preset.model_dump())
|
||||
|
||||
|
||||
@style_presets_router.get(
|
||||
"/",
|
||||
operation_id="list_style_presets",
|
||||
responses={
|
||||
200: {"model": list[StylePresetRecordDTO]},
|
||||
200: {"model": list[StylePresetRecordWithImage]},
|
||||
},
|
||||
)
|
||||
async def list_style_presets() -> list[StylePresetRecordDTO]:
|
||||
async def list_style_presets() -> list[StylePresetRecordWithImage]:
|
||||
"""Gets a page of style presets"""
|
||||
return ApiDependencies.invoker.services.style_preset_records.get_many()
|
||||
style_presets_with_image: list[StylePresetRecordWithImage] = []
|
||||
style_presets = ApiDependencies.invoker.services.style_preset_records.get_many()
|
||||
for preset in style_presets:
|
||||
image = ApiDependencies.invoker.services.style_preset_images_service.get_url(preset.id)
|
||||
style_preset_with_image = StylePresetRecordWithImage(image=image, **preset.model_dump())
|
||||
style_presets_with_image.append(style_preset_with_image)
|
||||
|
||||
return style_presets_with_image
|
||||
|
||||
|
||||
@style_presets_router.get(
|
||||
"/i/{style_preset_id}/image",
|
||||
operation_id="get_style_preset_image",
|
||||
responses={
|
||||
200: {
|
||||
"description": "The style preset image was fetched successfully",
|
||||
},
|
||||
400: {"description": "Bad request"},
|
||||
404: {"description": "The style preset image could not be found"},
|
||||
},
|
||||
status_code=200,
|
||||
)
|
||||
async def get_style_preset_image(
|
||||
style_preset_id: str = Path(description="The id of the style preset image to get"),
|
||||
) -> FileResponse:
|
||||
"""Gets an image file that previews the model"""
|
||||
|
||||
try:
|
||||
path = ApiDependencies.invoker.services.style_preset_images_service.get_path(style_preset_id)
|
||||
|
||||
response = FileResponse(
|
||||
path,
|
||||
media_type="image/png",
|
||||
filename=style_preset_id + ".png",
|
||||
content_disposition_type="inline",
|
||||
)
|
||||
response.headers["Cache-Control"] = f"max-age={IMAGE_MAX_AGE}"
|
||||
return response
|
||||
except Exception:
|
||||
raise HTTPException(status_code=404)
|
||||
|
@ -153,6 +153,7 @@ class InvokeAIAppConfig(BaseSettings):
|
||||
db_dir: Path = Field(default=Path("databases"), description="Path to InvokeAI databases directory.")
|
||||
outputs_dir: Path = Field(default=Path("outputs"), description="Path to directory for outputs.")
|
||||
custom_nodes_dir: Path = Field(default=Path("nodes"), description="Path to directory for custom nodes.")
|
||||
style_preset_images_path: Path = Field(default=Path("style_preset_images"), description="Path to directory for style preset images.")
|
||||
|
||||
# LOGGING
|
||||
log_handlers: list[str] = Field(default=["console"], description='Log handler. Valid options are "console", "file=<path>", "syslog=path|address:host:port", "http=<url>".')
|
||||
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from invokeai.app.services.object_serializer.object_serializer_base import ObjectSerializerBase
|
||||
from invokeai.app.services.style_preset_images.style_preset_images_base import StylePresetImageFileStorageBase
|
||||
from invokeai.app.services.style_preset_records.style_preset_records_base import StylePresetRecordsStorageBase
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -63,6 +64,7 @@ class InvocationServices:
|
||||
tensors: "ObjectSerializerBase[torch.Tensor]",
|
||||
conditioning: "ObjectSerializerBase[ConditioningFieldData]",
|
||||
style_preset_records: "StylePresetRecordsStorageBase",
|
||||
style_preset_images_service: "StylePresetImageFileStorageBase",
|
||||
):
|
||||
self.board_images = board_images
|
||||
self.board_image_records = board_image_records
|
||||
@ -88,3 +90,4 @@ class InvocationServices:
|
||||
self.tensors = tensors
|
||||
self.conditioning = conditioning
|
||||
self.style_preset_records = style_preset_records
|
||||
self.style_preset_images_service = style_preset_images_service
|
||||
|
@ -0,0 +1,33 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
|
||||
from PIL.Image import Image as PILImageType
|
||||
|
||||
|
||||
class StylePresetImageFileStorageBase(ABC):
|
||||
"""Low-level service responsible for storing and retrieving image files."""
|
||||
|
||||
@abstractmethod
|
||||
def get(self, style_preset_id: str) -> PILImageType:
|
||||
"""Retrieves a style preset image as PIL Image."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_path(self, style_preset_id: str) -> Path:
|
||||
"""Gets the internal path to a style preset image."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_url(self, style_preset_id: str) -> str | None:
|
||||
"""Gets the URL to fetch a style preset image."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save(self, image: PILImageType, style_preset_id: str) -> None:
|
||||
"""Saves a style preset image."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def delete(self, style_preset_id: str) -> None:
|
||||
"""Deletes a style preset image."""
|
||||
pass
|
@ -0,0 +1,19 @@
|
||||
class StylePresetImageFileNotFoundException(Exception):
|
||||
"""Raised when an image file is not found in storage."""
|
||||
|
||||
def __init__(self, message="Style preset image file not found"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class StylePresetImageFileSaveException(Exception):
|
||||
"""Raised when an image cannot be saved."""
|
||||
|
||||
def __init__(self, message="Style preset image file not saved"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class StylePresetImageFileDeleteException(Exception):
|
||||
"""Raised when an image cannot be deleted."""
|
||||
|
||||
def __init__(self, message="Style preset image file not deleted"):
|
||||
super().__init__(message)
|
@ -0,0 +1,84 @@
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image
|
||||
from PIL.Image import Image as PILImageType
|
||||
from send2trash import send2trash
|
||||
|
||||
from invokeai.app.services.invoker import Invoker
|
||||
from invokeai.app.services.style_preset_images.style_preset_images_base import StylePresetImageFileStorageBase
|
||||
from invokeai.app.services.style_preset_images.style_preset_images_common import (
|
||||
StylePresetImageFileDeleteException,
|
||||
StylePresetImageFileNotFoundException,
|
||||
StylePresetImageFileSaveException,
|
||||
)
|
||||
from invokeai.app.util.misc import uuid_string
|
||||
from invokeai.app.util.thumbnails import make_thumbnail
|
||||
|
||||
|
||||
class StylePresetImageFileStorageDisk(StylePresetImageFileStorageBase):
|
||||
"""Stores images on disk"""
|
||||
|
||||
def __init__(self, style_preset_images_folder: Path):
|
||||
self._style_preset_images_folder = style_preset_images_folder
|
||||
self._validate_storage_folders()
|
||||
|
||||
def start(self, invoker: Invoker) -> None:
|
||||
self._invoker = invoker
|
||||
|
||||
def get(self, style_preset_id: str) -> PILImageType:
|
||||
try:
|
||||
path = self.get_path(style_preset_id)
|
||||
|
||||
if not self._validate_path(path):
|
||||
raise StylePresetImageFileNotFoundException
|
||||
|
||||
return Image.open(path)
|
||||
except FileNotFoundError as e:
|
||||
raise StylePresetImageFileNotFoundException from e
|
||||
|
||||
def save(self, image: PILImageType, style_preset_id: str) -> None:
|
||||
try:
|
||||
self._validate_storage_folders()
|
||||
image_path = self._style_preset_images_folder / (style_preset_id + ".webp")
|
||||
thumbnail = make_thumbnail(image, 256)
|
||||
thumbnail.save(image_path, format="webp")
|
||||
|
||||
except Exception as e:
|
||||
raise StylePresetImageFileSaveException from e
|
||||
|
||||
def get_path(self, style_preset_id: str) -> Path:
|
||||
path = self._style_preset_images_folder / (style_preset_id + ".webp")
|
||||
|
||||
return path
|
||||
|
||||
def get_url(self, style_preset_id: str) -> str | None:
|
||||
path = self.get_path(style_preset_id)
|
||||
if not self._validate_path(path):
|
||||
return
|
||||
|
||||
url = self._invoker.services.urls.get_style_preset_image_url(style_preset_id)
|
||||
|
||||
# The image URL never changes, so we must add random query string to it to prevent caching
|
||||
url += f"?{uuid_string()}"
|
||||
|
||||
return url
|
||||
|
||||
def delete(self, style_preset_id: str) -> None:
|
||||
try:
|
||||
path = self.get_path(style_preset_id)
|
||||
|
||||
if not self._validate_path(path):
|
||||
raise StylePresetImageFileNotFoundException
|
||||
|
||||
send2trash(path)
|
||||
|
||||
except Exception as e:
|
||||
raise StylePresetImageFileDeleteException from e
|
||||
|
||||
def _validate_path(self, path: Path) -> bool:
|
||||
"""Validates the path given for an image."""
|
||||
return path.exists()
|
||||
|
||||
def _validate_storage_folders(self) -> None:
|
||||
"""Checks if the required folders exist and create them if they don't"""
|
||||
self._style_preset_images_folder.mkdir(parents=True, exist_ok=True)
|
@ -39,3 +39,7 @@ class StylePresetRecordDTO(StylePresetWithoutId):
|
||||
|
||||
|
||||
StylePresetRecordDTOValidator = TypeAdapter(StylePresetRecordDTO)
|
||||
|
||||
|
||||
class StylePresetRecordWithImage(StylePresetRecordDTO):
|
||||
image: Optional[str] = Field(description="The path for image")
|
||||
|
@ -13,3 +13,8 @@ class UrlServiceBase(ABC):
|
||||
def get_model_image_url(self, model_key: str) -> str:
|
||||
"""Gets the URL for a model image"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_style_preset_image_url(self, style_preset_id: str) -> str:
|
||||
"""Gets the URL for a style preset image"""
|
||||
pass
|
||||
|
@ -19,3 +19,6 @@ class LocalUrlService(UrlServiceBase):
|
||||
|
||||
def get_model_image_url(self, model_key: str) -> str:
|
||||
return f"{self._base_url_v2}/models/i/{model_key}/image"
|
||||
|
||||
def get_style_preset_image_url(self, style_preset_id: str) -> str:
|
||||
return f"{self._base_url}/style_presets/i/{style_preset_id}/image"
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
PiFlowArrowBold,
|
||||
PiFoldersBold,
|
||||
PiImagesBold,
|
||||
PiPaintBrushBold,
|
||||
PiPlantBold,
|
||||
PiQuotesBold,
|
||||
PiShareFatBold,
|
||||
@ -39,6 +40,8 @@ import {
|
||||
} from 'react-icons/pi';
|
||||
import { useStarImagesMutation, useUnstarImagesMutation } from 'services/api/endpoints/images';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
import { isMenuOpenChanged } from '../../../stylePresets/store/stylePresetSlice';
|
||||
import { createPresetFromImageChanged } from '../../../stylePresets/store/stylePresetModalSlice';
|
||||
|
||||
type SingleSelectionMenuItemsProps = {
|
||||
imageDTO: ImageDTO;
|
||||
@ -130,6 +133,11 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
||||
dispatch(setActiveTab('upscaling'));
|
||||
}, [dispatch, imageDTO]);
|
||||
|
||||
const handleCreatePreset = useCallback(() => {
|
||||
dispatch(createPresetFromImageChanged(imageDTO));
|
||||
dispatch(isMenuOpenChanged(true));
|
||||
}, [dispatch, imageDTO]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuItem as="a" href={imageDTO.image_url} target="_blank" icon={<PiShareFatBold />}>
|
||||
@ -182,6 +190,13 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
||||
>
|
||||
{t('parameters.useAll')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={isLoadingMetadata ? <SpinnerIcon /> : <PiPaintBrushBold />}
|
||||
onClickCapture={handleCreatePreset}
|
||||
isDisabled={isLoadingMetadata || !hasPrompts}
|
||||
>
|
||||
Create Preset
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem icon={<PiShareFatBold />} onClickCapture={handleSendToImageToImage} id="send-to-img2img">
|
||||
{t('parameters.sendToImg2Img')}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { negativePromptChanged, positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||
import ModelImage from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage';
|
||||
import { usePresetModifiedPrompts } from 'features/stylePresets/hooks/usePresetModifiedPrompts';
|
||||
import { activeStylePresetChanged } from 'features/stylePresets/store/stylePresetSlice';
|
||||
import type { MouseEventHandler} from 'react';
|
||||
import type { MouseEventHandler } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { CgPushDown } from 'react-icons/cg';
|
||||
import { PiXBold } from 'react-icons/pi';
|
||||
import StylePresetImage from './StylePresetImage';
|
||||
|
||||
export const ActiveStylePreset = () => {
|
||||
const { activeStylePreset } = useAppSelector((s) => s.stylePreset);
|
||||
@ -40,7 +40,7 @@ export const ActiveStylePreset = () => {
|
||||
<>
|
||||
<Flex justifyContent="space-between" w="full" alignItems="center">
|
||||
<Flex gap="2">
|
||||
<ModelImage image_url={null} />
|
||||
<StylePresetImage presetImageUrl={activeStylePreset.image} />
|
||||
<Flex flexDir="column">
|
||||
<Text variant="subtext" fontSize="xs">
|
||||
Prompt Style
|
||||
|
@ -4,21 +4,23 @@ import { useStylePresetFields } from 'features/stylePresets/hooks/useStylePreset
|
||||
import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/stylePresetModalSlice';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback } from 'react';
|
||||
import type { SubmitHandler} from 'react-hook-form';
|
||||
import type { SubmitHandler } from 'react-hook-form';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { PiBracketsCurlyBold } from 'react-icons/pi';
|
||||
import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets';
|
||||
import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
|
||||
import { useCreateStylePresetMutation, useUpdateStylePresetMutation } from 'services/api/endpoints/stylePresets';
|
||||
|
||||
import { StylePresetPromptField } from './StylePresetPromptField';
|
||||
import { StylePresetImageField } from './StylePresetImageField';
|
||||
|
||||
export type StylePresetFormData = {
|
||||
name: string;
|
||||
positivePrompt: string;
|
||||
negativePrompt: string;
|
||||
image: File | null;
|
||||
};
|
||||
|
||||
export const StylePresetForm = ({ updatingPreset }: { updatingPreset: StylePresetRecordDTO | null }) => {
|
||||
export const StylePresetForm = ({ updatingPreset }: { updatingPreset: StylePresetRecordWithImage | null }) => {
|
||||
const [createStylePreset] = useCreateStylePresetMutation();
|
||||
const [updateStylePreset] = useUpdateStylePresetMutation();
|
||||
const dispatch = useAppDispatch();
|
||||
@ -31,20 +33,21 @@ export const StylePresetForm = ({ updatingPreset }: { updatingPreset: StylePrese
|
||||
|
||||
const handleClickSave = useCallback<SubmitHandler<StylePresetFormData>>(
|
||||
async (data) => {
|
||||
const payload = {
|
||||
name: data.name,
|
||||
positive_prompt: data.positivePrompt,
|
||||
negative_prompt: data.negativePrompt,
|
||||
image: data.image,
|
||||
};
|
||||
|
||||
try {
|
||||
if (updatingPreset) {
|
||||
await updateStylePreset({
|
||||
id: updatingPreset.id,
|
||||
changes: {
|
||||
name: data.name,
|
||||
preset_data: { positive_prompt: data.positivePrompt, negative_prompt: data.negativePrompt },
|
||||
},
|
||||
...payload,
|
||||
}).unwrap();
|
||||
} else {
|
||||
await createStylePreset({
|
||||
name: data.name,
|
||||
preset_data: { positive_prompt: data.positivePrompt, negative_prompt: data.negativePrompt },
|
||||
}).unwrap();
|
||||
await createStylePreset(payload).unwrap();
|
||||
}
|
||||
} catch (error) {
|
||||
toast({
|
||||
@ -61,19 +64,21 @@ export const StylePresetForm = ({ updatingPreset }: { updatingPreset: StylePrese
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" gap="4">
|
||||
<FormControl>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<Input size="md" {...register('name')} />
|
||||
</FormControl>
|
||||
<Flex flexDir="column" bgColor="base.750" borderRadius="base" padding="10px" gap="10px">
|
||||
<Text variant="subtext">
|
||||
Use the <Icon as={PiBracketsCurlyBold} /> button to specify where your manual prompt should be included in the
|
||||
template. If you do not provide one, the template will be appended to your prompt.
|
||||
</Text>
|
||||
<StylePresetPromptField label="Positive Prompt" control={control} name="positivePrompt" />
|
||||
<StylePresetPromptField label="Negative Prompt" control={control} name="negativePrompt" />
|
||||
<Flex alignItems="center" gap="4">
|
||||
<StylePresetImageField control={control} name="image" />
|
||||
<FormControl orientation="vertical">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<Input size="md" {...register('name')} />
|
||||
</FormControl>
|
||||
</Flex>
|
||||
|
||||
<StylePresetPromptField label="Positive Prompt" control={control} name="positivePrompt" />
|
||||
<StylePresetPromptField label="Negative Prompt" control={control} name="negativePrompt" />
|
||||
<Text variant="subtext">
|
||||
Use the <Icon as={PiBracketsCurlyBold} /> button to specify where your manual prompt should be included in the
|
||||
template. If you do not provide one, the template will be appended to your prompt.
|
||||
</Text>
|
||||
|
||||
<Button onClick={handleSubmit(handleClickSave)}>Save</Button>
|
||||
</Flex>
|
||||
);
|
||||
|
@ -0,0 +1,36 @@
|
||||
import { Flex, Icon, Image } from '@invoke-ai/ui-library';
|
||||
import { typedMemo } from 'common/util/typedMemo';
|
||||
import { PiImage } from 'react-icons/pi';
|
||||
|
||||
const IMAGE_THUMBNAIL_SIZE = '40px';
|
||||
const FALLBACK_ICON_SIZE = '24px';
|
||||
|
||||
const StylePresetImage = ({ presetImageUrl }: { presetImageUrl: string | null }) => {
|
||||
return (
|
||||
<Image
|
||||
src={presetImageUrl || ''}
|
||||
fallbackStrategy="beforeLoadOrError"
|
||||
fallback={
|
||||
<Flex
|
||||
height={IMAGE_THUMBNAIL_SIZE}
|
||||
minWidth={IMAGE_THUMBNAIL_SIZE}
|
||||
bg="base.650"
|
||||
borderRadius="base"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Icon color="base.500" as={PiImage} boxSize={FALLBACK_ICON_SIZE} />
|
||||
</Flex>
|
||||
}
|
||||
objectFit="cover"
|
||||
objectPosition="50% 50%"
|
||||
height={IMAGE_THUMBNAIL_SIZE}
|
||||
width={IMAGE_THUMBNAIL_SIZE}
|
||||
minHeight={IMAGE_THUMBNAIL_SIZE}
|
||||
minWidth={IMAGE_THUMBNAIL_SIZE}
|
||||
borderRadius="base"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default typedMemo(StylePresetImage);
|
@ -0,0 +1,79 @@
|
||||
import { Tooltip, Flex, Button, Icon, Box, Image, IconButton } from '@invoke-ai/ui-library';
|
||||
import { t } from 'i18next';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { PiArrowCounterClockwiseBold, PiUploadSimpleBold } from 'react-icons/pi';
|
||||
import { useController, UseControllerProps } from 'react-hook-form';
|
||||
import { StylePresetFormData } from './StylePresetForm';
|
||||
|
||||
export const StylePresetImageField = (props: UseControllerProps<StylePresetFormData>) => {
|
||||
const { field } = useController(props);
|
||||
const onDropAccepted = useCallback(
|
||||
(files: File[]) => {
|
||||
const file = files[0];
|
||||
if (file) {
|
||||
field.onChange(file);
|
||||
}
|
||||
},
|
||||
[field, t]
|
||||
);
|
||||
|
||||
const handleResetImage = useCallback(() => {
|
||||
field.onChange(null);
|
||||
}, []);
|
||||
|
||||
const { getInputProps, getRootProps } = useDropzone({
|
||||
accept: { 'image/png': ['.png'], 'image/jpeg': ['.jpg', '.jpeg', '.png'] },
|
||||
onDropAccepted,
|
||||
noDrag: true,
|
||||
multiple: false,
|
||||
});
|
||||
|
||||
if (field.value) {
|
||||
return (
|
||||
<Box position="relative" flexShrink={0}>
|
||||
<Image
|
||||
src={URL.createObjectURL(field.value as File)}
|
||||
objectFit="cover"
|
||||
objectPosition="50% 50%"
|
||||
w={65}
|
||||
h={65}
|
||||
minWidth={65}
|
||||
borderRadius="base"
|
||||
/>
|
||||
<IconButton
|
||||
position="absolute"
|
||||
insetInlineEnd={0}
|
||||
insetBlockStart={0}
|
||||
onClick={handleResetImage}
|
||||
aria-label={t('modelManager.deleteModelImage')}
|
||||
tooltip={t('modelManager.deleteModelImage')}
|
||||
icon={<PiArrowCounterClockwiseBold />}
|
||||
size="md"
|
||||
variant="ghost"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip label={t('modelManager.uploadImage')}>
|
||||
<Flex
|
||||
as={Button}
|
||||
w={65}
|
||||
h={65}
|
||||
opacity={0.3}
|
||||
borderRadius="base"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
flexShrink={0}
|
||||
{...getRootProps()}
|
||||
>
|
||||
<Icon as={PiUploadSimpleBold} w={8} h={8} />
|
||||
</Flex>
|
||||
</Tooltip>
|
||||
<input {...getInputProps()} />
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,10 +1,10 @@
|
||||
import { Button, Collapse, Flex, Icon, Text, useDisclosure } from '@invoke-ai/ui-library';
|
||||
import { PiCaretDownBold } from 'react-icons/pi';
|
||||
import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets';
|
||||
import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
|
||||
|
||||
import { StylePresetListItem } from './StylePresetListItem';
|
||||
|
||||
export const StylePresetList = ({ title, data }: { title: string; data: StylePresetRecordDTO[] }) => {
|
||||
export const StylePresetList = ({ title, data }: { title: string; data: StylePresetRecordWithImage[] }) => {
|
||||
const { onToggle, isOpen } = useDisclosure({ defaultIsOpen: true });
|
||||
|
||||
if (!data.length) {
|
||||
|
@ -1,33 +1,43 @@
|
||||
import { Badge, Flex, IconButton, Text } from '@invoke-ai/ui-library';
|
||||
import type { MouseEvent } from 'react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import ModelImage from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage';
|
||||
import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/stylePresetModalSlice';
|
||||
import { activeStylePresetChanged, isMenuOpenChanged } from 'features/stylePresets/store/stylePresetSlice';
|
||||
import { useCallback } from 'react';
|
||||
import { PiPencilBold, PiTrashBold } from 'react-icons/pi';
|
||||
import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets';
|
||||
import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
|
||||
import { useDeleteStylePresetMutation } from 'services/api/endpoints/stylePresets';
|
||||
import StylePresetImage from './StylePresetImage';
|
||||
|
||||
export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordDTO }) => {
|
||||
export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithImage }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [deleteStylePreset] = useDeleteStylePresetMutation();
|
||||
const activeStylePreset = useAppSelector((s) => s.stylePreset.activeStylePreset);
|
||||
|
||||
const handleClickEdit = useCallback(() => {
|
||||
dispatch(updatingStylePresetChanged(preset));
|
||||
dispatch(isModalOpenChanged(true));
|
||||
}, [dispatch, preset]);
|
||||
const handleClickEdit = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
dispatch(updatingStylePresetChanged(preset));
|
||||
dispatch(isModalOpenChanged(true));
|
||||
},
|
||||
[dispatch, preset]
|
||||
);
|
||||
|
||||
const handleClickApply = useCallback(() => {
|
||||
dispatch(activeStylePresetChanged(preset));
|
||||
dispatch(isMenuOpenChanged(false));
|
||||
}, [dispatch, preset]);
|
||||
|
||||
const handleDeletePreset = useCallback(async () => {
|
||||
try {
|
||||
await deleteStylePreset(preset.id);
|
||||
} catch (error) {}
|
||||
}, [preset]);
|
||||
const handleDeletePreset = useCallback(
|
||||
async (e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
await deleteStylePreset(preset.id);
|
||||
} catch (error) {}
|
||||
},
|
||||
[preset]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@ -40,7 +50,7 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordDTO }
|
||||
alignItems="center"
|
||||
w="full"
|
||||
>
|
||||
<ModelImage image_url={null} />
|
||||
<StylePresetImage presetImageUrl={preset.image} />
|
||||
<Flex flexDir="column" w="full">
|
||||
<Flex w="full" justifyContent="space-between">
|
||||
<Flex alignItems="center" gap="2">
|
||||
|
@ -4,7 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/stylePresetModalSlice';
|
||||
import { useCallback } from 'react';
|
||||
import { PiPlusBold } from 'react-icons/pi';
|
||||
import type { StylePresetRecordDTO} from 'services/api/endpoints/stylePresets';
|
||||
import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
|
||||
import { useListStylePresetsQuery } from 'services/api/endpoints/stylePresets';
|
||||
|
||||
import { StylePresetList } from './StylePresetList';
|
||||
@ -18,7 +18,7 @@ export const StylePresetMenu = () => {
|
||||
data?.filter((preset) => preset.name.toLowerCase().includes(searchTerm.toLowerCase())) || EMPTY_ARRAY;
|
||||
|
||||
const groupedData = filteredData.reduce(
|
||||
(acc: { defaultPresets: StylePresetRecordDTO[]; presets: StylePresetRecordDTO[] }, preset) => {
|
||||
(acc: { defaultPresets: StylePresetRecordWithImage[]; presets: StylePresetRecordWithImage[] }, preset) => {
|
||||
if (preset.is_default) {
|
||||
acc.defaultPresets.push(preset);
|
||||
} else {
|
||||
|
@ -24,7 +24,7 @@ export const StylePresetPromptField = (props: Props) => {
|
||||
);
|
||||
|
||||
const value = useMemo(() => {
|
||||
return field.value;
|
||||
return field.value as string;
|
||||
}, [field.value]);
|
||||
|
||||
const insertPromptPlaceholder = useCallback(() => {
|
||||
@ -40,7 +40,7 @@ export const StylePresetPromptField = (props: Props) => {
|
||||
}
|
||||
}, [value, field, textareaRef]);
|
||||
|
||||
const isPromptPresent = useMemo(() => value.includes(PRESET_PLACEHOLDER), [value]);
|
||||
const isPromptPresent = useMemo(() => value?.includes(PRESET_PLACEHOLDER), [value]);
|
||||
|
||||
return (
|
||||
<FormControl orientation="vertical">
|
||||
|
@ -1,17 +1,45 @@
|
||||
import { useMemo } from 'react';
|
||||
import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
|
||||
import { useAppSelector } from '../../../app/store/storeHooks';
|
||||
import { useDebouncedMetadata } from '../../../services/api/hooks/useDebouncedMetadata';
|
||||
import { handlers } from '../../metadata/util/handlers';
|
||||
import { useImageUrlToBlob } from '../../../common/hooks/useImageUrlToBlob';
|
||||
|
||||
|
||||
export const useStylePresetFields = (preset: StylePresetRecordWithImage | null) => {
|
||||
const createPresetFromImage = useAppSelector(s => s.stylePresetModal.createPresetFromImage)
|
||||
|
||||
const imageUrlToBlob = useImageUrlToBlob();
|
||||
|
||||
const getStylePresetFieldDefaults = useCallback(async () => {
|
||||
if (preset) {
|
||||
let file: File | null = null;
|
||||
if (preset.image) {
|
||||
const blob = await imageUrlToBlob(preset.image);
|
||||
if (blob) {
|
||||
file = new File([blob], "name");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
name: preset.name,
|
||||
positivePrompt: preset.preset_data.positive_prompt,
|
||||
negativePrompt: preset.preset_data.negative_prompt,
|
||||
image: file
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export const useStylePresetFields = (preset: StylePresetRecordDTO | null) => {
|
||||
const stylePresetFieldDefaults = useMemo(() => {
|
||||
return {
|
||||
name: preset ? preset.name : '',
|
||||
positivePrompt: preset ? preset.preset_data.positive_prompt : '',
|
||||
negativePrompt: preset ? preset.preset_data.negative_prompt : ''
|
||||
name: "",
|
||||
positivePrompt: "",
|
||||
negativePrompt: "",
|
||||
image: null
|
||||
};
|
||||
}, [
|
||||
preset
|
||||
]);
|
||||
|
||||
return stylePresetFieldDefaults;
|
||||
return getStylePresetFieldDefaults;
|
||||
};
|
||||
|
@ -1,14 +1,16 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets';
|
||||
import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
|
||||
|
||||
import type { StylePresetModalState } from './types';
|
||||
import type { StylePresetModalState, StylePresetPrefillOptions } from './types';
|
||||
import { ImageDTO } from '../../../services/api/types';
|
||||
|
||||
|
||||
export const initialState: StylePresetModalState = {
|
||||
isModalOpen: false,
|
||||
updatingStylePreset: null,
|
||||
createPresetFromImage: null
|
||||
};
|
||||
|
||||
|
||||
@ -19,12 +21,15 @@ export const stylePresetModalSlice = createSlice({
|
||||
isModalOpenChanged: (state, action: PayloadAction<boolean>) => {
|
||||
state.isModalOpen = action.payload;
|
||||
},
|
||||
updatingStylePresetChanged: (state, action: PayloadAction<StylePresetRecordDTO | null>) => {
|
||||
updatingStylePresetChanged: (state, action: PayloadAction<StylePresetRecordWithImage | null>) => {
|
||||
state.updatingStylePreset = action.payload;
|
||||
},
|
||||
createPresetFromImageChanged: (state, action: PayloadAction<ImageDTO | null>) => {
|
||||
state.createPresetFromImage = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { isModalOpenChanged, updatingStylePresetChanged } = stylePresetModalSlice.actions;
|
||||
export const { isModalOpenChanged, updatingStylePresetChanged, createPresetFromImageChanged } = stylePresetModalSlice.actions;
|
||||
|
||||
export const selectStylePresetModalSlice = (state: RootState) => state.stylePresetModal;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets';
|
||||
import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
|
||||
|
||||
import type { StylePresetState } from './types';
|
||||
|
||||
@ -20,7 +20,7 @@ export const stylePresetSlice = createSlice({
|
||||
isMenuOpenChanged: (state, action: PayloadAction<boolean>) => {
|
||||
state.isMenuOpen = action.payload;
|
||||
},
|
||||
activeStylePresetChanged: (state, action: PayloadAction<StylePresetRecordDTO | null>) => {
|
||||
activeStylePresetChanged: (state, action: PayloadAction<StylePresetRecordWithImage | null>) => {
|
||||
state.activeStylePreset = action.payload;
|
||||
},
|
||||
searchTermChanged: (state, action: PayloadAction<string>) => {
|
||||
|
@ -1,13 +1,21 @@
|
||||
import type { StylePresetRecordDTO } from "services/api/endpoints/stylePresets";
|
||||
import type { StylePresetRecordWithImage } from "services/api/endpoints/stylePresets";
|
||||
import { ImageDTO } from "../../../services/api/types";
|
||||
|
||||
export type StylePresetModalState = {
|
||||
isModalOpen: boolean;
|
||||
updatingStylePreset: StylePresetRecordDTO | null;
|
||||
updatingStylePreset: StylePresetRecordWithImage | null;
|
||||
createPresetFromImage: ImageDTO | null
|
||||
};
|
||||
|
||||
export type StylePresetPrefillOptions = {
|
||||
positivePrompt: string;
|
||||
negativePrompt: string;
|
||||
image: File;
|
||||
}
|
||||
|
||||
export type StylePresetState = {
|
||||
isMenuOpen: boolean;
|
||||
activeStylePreset: StylePresetRecordDTO | null;
|
||||
activeStylePreset: StylePresetRecordWithImage | null;
|
||||
searchTerm: string
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import type { paths } from 'services/api/schema';
|
||||
|
||||
import { api, buildV1Url, LIST_TAG } from '..';
|
||||
|
||||
export type StylePresetRecordDTO = paths['/api/v1/style_presets/i/{style_preset_id}']['get']['responses']['200']['content']['application/json']
|
||||
export type StylePresetRecordWithImage = paths['/api/v1/style_presets/i/{style_preset_id}']['get']['responses']['200']['content']['application/json']
|
||||
|
||||
/**
|
||||
* Builds an endpoint URL for the style_presets router
|
||||
@ -36,13 +36,23 @@ export const stylePresetsApi = api.injectEndpoints({
|
||||
}),
|
||||
createStylePreset: build.mutation<
|
||||
paths['/api/v1/style_presets/']['post']['responses']['200']['content']['application/json'],
|
||||
paths['/api/v1/style_presets/']['post']['requestBody']['content']['application/json']['style_preset']
|
||||
paths['/api/v1/style_presets/']['post']['requestBody']['content']['multipart/form-data']
|
||||
>({
|
||||
query: (style_preset) => ({
|
||||
url: buildStylePresetsUrl(),
|
||||
method: 'POST',
|
||||
body: { style_preset },
|
||||
}),
|
||||
query: ({ name, positive_prompt, negative_prompt, image }) => {
|
||||
const formData = new FormData();
|
||||
if (image) {
|
||||
formData.append('image', image);
|
||||
}
|
||||
formData.append('name', name);
|
||||
formData.append('positive_prompt', positive_prompt);
|
||||
formData.append('negative_prompt', negative_prompt);
|
||||
|
||||
return {
|
||||
url: buildStylePresetsUrl(),
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
};
|
||||
},
|
||||
invalidatesTags: [
|
||||
{ type: 'StylePreset', id: LIST_TAG },
|
||||
{ type: 'StylePreset', id: LIST_TAG },
|
||||
@ -50,16 +60,25 @@ export const stylePresetsApi = api.injectEndpoints({
|
||||
}),
|
||||
updateStylePreset: build.mutation<
|
||||
paths['/api/v1/style_presets/i/{style_preset_id}']['patch']['responses']['200']['content']['application/json'],
|
||||
{
|
||||
id: string;
|
||||
changes: paths['/api/v1/style_presets/i/{style_preset_id}']['patch']['requestBody']['content']['application/json']['changes'];
|
||||
}
|
||||
paths['/api/v1/style_presets/i/{style_preset_id}']['patch']['requestBody']['content']['multipart/form-data'] & { id: string }
|
||||
>({
|
||||
query: ({ id, changes }) => ({
|
||||
url: buildStylePresetsUrl(`i/${id}`),
|
||||
method: 'PATCH',
|
||||
body: { changes },
|
||||
}),
|
||||
query: ({ id, name, positive_prompt, negative_prompt, image }) => {
|
||||
const formData = new FormData();
|
||||
if (image) {
|
||||
formData.append('image', image);
|
||||
}
|
||||
|
||||
formData.append('name', name);
|
||||
formData.append('positive_prompt', positive_prompt);
|
||||
formData.append('negative_prompt', negative_prompt);
|
||||
|
||||
|
||||
return {
|
||||
url: buildStylePresetsUrl(`i/${id}`),
|
||||
method: 'PATCH',
|
||||
body: formData
|
||||
}
|
||||
},
|
||||
invalidatesTags: (response, error, { id }) => [
|
||||
{ type: 'StylePreset', id: LIST_TAG },
|
||||
{ type: 'StylePreset', id: id },
|
||||
|
@ -561,6 +561,13 @@ export type paths = {
|
||||
*/
|
||||
post: operations["create_style_preset"];
|
||||
};
|
||||
"/api/v1/style_presets/i/{style_preset_id}/image": {
|
||||
/**
|
||||
* Get Style Preset Image
|
||||
* @description Gets an image file that previews the model
|
||||
*/
|
||||
get: operations["get_style_preset_image"];
|
||||
};
|
||||
};
|
||||
|
||||
export type webhooks = Record<string, never>;
|
||||
@ -1146,8 +1153,26 @@ export type components = {
|
||||
};
|
||||
/** Body_create_style_preset */
|
||||
Body_create_style_preset: {
|
||||
/** @description The style preset to create */
|
||||
style_preset: components["schemas"]["StylePresetWithoutId"];
|
||||
/**
|
||||
* Name
|
||||
* @description The name of the style preset to create
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Positive Prompt
|
||||
* @description The positive prompt of the style preset
|
||||
*/
|
||||
positive_prompt: string;
|
||||
/**
|
||||
* Negative Prompt
|
||||
* @description The negative prompt of the style preset
|
||||
*/
|
||||
negative_prompt: string;
|
||||
/**
|
||||
* Image
|
||||
* @description The image file to upload
|
||||
*/
|
||||
image?: Blob | null;
|
||||
};
|
||||
/** Body_create_workflow */
|
||||
Body_create_workflow: {
|
||||
@ -1273,8 +1298,26 @@ export type components = {
|
||||
};
|
||||
/** Body_update_style_preset */
|
||||
Body_update_style_preset: {
|
||||
/** @description The updated style preset */
|
||||
changes: components["schemas"]["StylePresetChanges"];
|
||||
/**
|
||||
* Name
|
||||
* @description The name of the style preset to create
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Positive Prompt
|
||||
* @description The positive prompt of the style preset
|
||||
*/
|
||||
positive_prompt: string;
|
||||
/**
|
||||
* Negative Prompt
|
||||
* @description The negative prompt of the style preset
|
||||
*/
|
||||
negative_prompt: string;
|
||||
/**
|
||||
* Image
|
||||
* @description The image file to upload
|
||||
*/
|
||||
image?: Blob | null;
|
||||
};
|
||||
/** Body_update_workflow */
|
||||
Body_update_workflow: {
|
||||
@ -7345,147 +7388,147 @@ export type components = {
|
||||
project_id: string | null;
|
||||
};
|
||||
InvocationOutputMap: {
|
||||
esrgan: components["schemas"]["ImageOutput"];
|
||||
tiled_multi_diffusion_denoise_latents: components["schemas"]["LatentsOutput"];
|
||||
conditioning_collection: components["schemas"]["ConditioningCollectionOutput"];
|
||||
merge_metadata: components["schemas"]["MetadataOutput"];
|
||||
lresize: components["schemas"]["LatentsOutput"];
|
||||
string_split_neg: components["schemas"]["StringPosNegOutput"];
|
||||
img_channel_multiply: components["schemas"]["ImageOutput"];
|
||||
add: components["schemas"]["IntegerOutput"];
|
||||
lscale: components["schemas"]["LatentsOutput"];
|
||||
string_replace: components["schemas"]["StringOutput"];
|
||||
sdxl_model_loader: components["schemas"]["SDXLModelLoaderOutput"];
|
||||
collect: components["schemas"]["CollectInvocationOutput"];
|
||||
face_off: components["schemas"]["FaceOffOutput"];
|
||||
ideal_size: components["schemas"]["IdealSizeOutput"];
|
||||
float_to_int: components["schemas"]["IntegerOutput"];
|
||||
mlsd_image_processor: components["schemas"]["ImageOutput"];
|
||||
sub: components["schemas"]["IntegerOutput"];
|
||||
midas_depth_image_processor: components["schemas"]["ImageOutput"];
|
||||
rectangle_mask: components["schemas"]["MaskOutput"];
|
||||
img_watermark: components["schemas"]["ImageOutput"];
|
||||
img_ilerp: components["schemas"]["ImageOutput"];
|
||||
pidi_image_processor: components["schemas"]["ImageOutput"];
|
||||
vae_loader: components["schemas"]["VAEOutput"];
|
||||
dynamic_prompt: components["schemas"]["StringCollectionOutput"];
|
||||
float_range: components["schemas"]["FloatCollectionOutput"];
|
||||
spandrel_image_to_image_autoscale: components["schemas"]["ImageOutput"];
|
||||
create_denoise_mask: components["schemas"]["DenoiseMaskOutput"];
|
||||
denoise_latents: components["schemas"]["LatentsOutput"];
|
||||
image_collection: components["schemas"]["ImageCollectionOutput"];
|
||||
t2i_adapter: components["schemas"]["T2IAdapterOutput"];
|
||||
normalbae_image_processor: components["schemas"]["ImageOutput"];
|
||||
calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"];
|
||||
mul: components["schemas"]["IntegerOutput"];
|
||||
image_mask_to_tensor: components["schemas"]["MaskOutput"];
|
||||
face_mask_detection: components["schemas"]["FaceMaskOutput"];
|
||||
save_image: components["schemas"]["ImageOutput"];
|
||||
blank_image: components["schemas"]["ImageOutput"];
|
||||
conditioning: components["schemas"]["ConditioningOutput"];
|
||||
img_chan: components["schemas"]["ImageOutput"];
|
||||
string_split: components["schemas"]["String2Output"];
|
||||
segment_anything_processor: components["schemas"]["ImageOutput"];
|
||||
unsharp_mask: components["schemas"]["ImageOutput"];
|
||||
boolean_collection: components["schemas"]["BooleanCollectionOutput"];
|
||||
color: components["schemas"]["ColorOutput"];
|
||||
range_of_size: components["schemas"]["IntegerCollectionOutput"];
|
||||
face_identifier: components["schemas"]["ImageOutput"];
|
||||
div: components["schemas"]["IntegerOutput"];
|
||||
invert_tensor_mask: components["schemas"]["MaskOutput"];
|
||||
step_param_easing: components["schemas"]["FloatCollectionOutput"];
|
||||
merge_tiles_to_image: components["schemas"]["ImageOutput"];
|
||||
latents: components["schemas"]["LatentsOutput"];
|
||||
lineart_anime_image_processor: components["schemas"]["ImageOutput"];
|
||||
mediapipe_face_processor: components["schemas"]["ImageOutput"];
|
||||
infill_lama: components["schemas"]["ImageOutput"];
|
||||
compel: components["schemas"]["ConditioningOutput"];
|
||||
round_float: components["schemas"]["FloatOutput"];
|
||||
string_join: components["schemas"]["StringOutput"];
|
||||
dw_openpose_image_processor: components["schemas"]["ImageOutput"];
|
||||
content_shuffle_image_processor: components["schemas"]["ImageOutput"];
|
||||
metadata: components["schemas"]["MetadataOutput"];
|
||||
lineart_image_processor: components["schemas"]["ImageOutput"];
|
||||
zoe_depth_image_processor: components["schemas"]["ImageOutput"];
|
||||
controlnet: components["schemas"]["ControlOutput"];
|
||||
sdxl_lora_collection_loader: components["schemas"]["SDXLLoRALoaderOutput"];
|
||||
img_lerp: components["schemas"]["ImageOutput"];
|
||||
scheduler: components["schemas"]["SchedulerOutput"];
|
||||
l2i: components["schemas"]["ImageOutput"];
|
||||
img_paste: components["schemas"]["ImageOutput"];
|
||||
tile_to_properties: components["schemas"]["TileToPropertiesOutput"];
|
||||
sdxl_refiner_compel_prompt: components["schemas"]["ConditioningOutput"];
|
||||
calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"];
|
||||
depth_anything_image_processor: components["schemas"]["ImageOutput"];
|
||||
prompt_from_file: components["schemas"]["StringCollectionOutput"];
|
||||
integer: components["schemas"]["IntegerOutput"];
|
||||
img_mul: components["schemas"]["ImageOutput"];
|
||||
color_map_image_processor: components["schemas"]["ImageOutput"];
|
||||
rand_int: components["schemas"]["IntegerOutput"];
|
||||
string_join_three: components["schemas"]["StringOutput"];
|
||||
img_blur: components["schemas"]["ImageOutput"];
|
||||
float: components["schemas"]["FloatOutput"];
|
||||
sdxl_lora_loader: components["schemas"]["SDXLLoRALoaderOutput"];
|
||||
crop_latents: components["schemas"]["LatentsOutput"];
|
||||
model_identifier: components["schemas"]["ModelIdentifierOutput"];
|
||||
show_image: components["schemas"]["ImageOutput"];
|
||||
img_channel_offset: components["schemas"]["ImageOutput"];
|
||||
i2l: components["schemas"]["LatentsOutput"];
|
||||
core_metadata: components["schemas"]["MetadataOutput"];
|
||||
infill_cv2: components["schemas"]["ImageOutput"];
|
||||
lora_selector: components["schemas"]["LoRASelectorOutput"];
|
||||
rand_float: components["schemas"]["FloatOutput"];
|
||||
float_math: components["schemas"]["FloatOutput"];
|
||||
main_model_loader: components["schemas"]["ModelLoaderOutput"];
|
||||
image: components["schemas"]["ImageOutput"];
|
||||
infill_patchmatch: components["schemas"]["ImageOutput"];
|
||||
noise: components["schemas"]["NoiseOutput"];
|
||||
tile_image_processor: components["schemas"]["ImageOutput"];
|
||||
spandrel_image_to_image: components["schemas"]["ImageOutput"];
|
||||
hed_image_processor: components["schemas"]["ImageOutput"];
|
||||
heuristic_resize: components["schemas"]["ImageOutput"];
|
||||
img_hue_adjust: components["schemas"]["ImageOutput"];
|
||||
create_gradient_mask: components["schemas"]["GradientMaskOutput"];
|
||||
img_scale: components["schemas"]["ImageOutput"];
|
||||
random_range: components["schemas"]["IntegerCollectionOutput"];
|
||||
latents: components["schemas"]["LatentsOutput"];
|
||||
cv_inpaint: components["schemas"]["ImageOutput"];
|
||||
float_range: components["schemas"]["FloatCollectionOutput"];
|
||||
img_pad_crop: components["schemas"]["ImageOutput"];
|
||||
range_of_size: components["schemas"]["IntegerCollectionOutput"];
|
||||
vae_loader: components["schemas"]["VAEOutput"];
|
||||
lora_collection_loader: components["schemas"]["LoRALoaderOutput"];
|
||||
freeu: components["schemas"]["UNetOutput"];
|
||||
float_collection: components["schemas"]["FloatCollectionOutput"];
|
||||
sdxl_compel_prompt: components["schemas"]["ConditioningOutput"];
|
||||
calculate_image_tiles_min_overlap: components["schemas"]["CalculateImageTilesOutput"];
|
||||
canvas_paste_back: components["schemas"]["ImageOutput"];
|
||||
ip_adapter: components["schemas"]["IPAdapterOutput"];
|
||||
img_nsfw: components["schemas"]["ImageOutput"];
|
||||
range: components["schemas"]["IntegerCollectionOutput"];
|
||||
string_replace: components["schemas"]["StringOutput"];
|
||||
boolean: components["schemas"]["BooleanOutput"];
|
||||
show_image: components["schemas"]["ImageOutput"];
|
||||
img_hue_adjust: components["schemas"]["ImageOutput"];
|
||||
metadata: components["schemas"]["MetadataOutput"];
|
||||
img_conv: components["schemas"]["ImageOutput"];
|
||||
sub: components["schemas"]["IntegerOutput"];
|
||||
pair_tile_image: components["schemas"]["PairTileImageOutput"];
|
||||
save_image: components["schemas"]["ImageOutput"];
|
||||
rectangle_mask: components["schemas"]["MaskOutput"];
|
||||
ideal_size: components["schemas"]["IdealSizeOutput"];
|
||||
lresize: components["schemas"]["LatentsOutput"];
|
||||
lblend: components["schemas"]["LatentsOutput"];
|
||||
conditioning_collection: components["schemas"]["ConditioningCollectionOutput"];
|
||||
rand_float: components["schemas"]["FloatOutput"];
|
||||
prompt_from_file: components["schemas"]["StringCollectionOutput"];
|
||||
tomask: components["schemas"]["ImageOutput"];
|
||||
boolean_collection: components["schemas"]["BooleanCollectionOutput"];
|
||||
color_correct: components["schemas"]["ImageOutput"];
|
||||
img_channel_offset: components["schemas"]["ImageOutput"];
|
||||
compel: components["schemas"]["ConditioningOutput"];
|
||||
infill_tile: components["schemas"]["ImageOutput"];
|
||||
img_resize: components["schemas"]["ImageOutput"];
|
||||
create_denoise_mask: components["schemas"]["DenoiseMaskOutput"];
|
||||
sdxl_refiner_model_loader: components["schemas"]["SDXLRefinerModelLoaderOutput"];
|
||||
mlsd_image_processor: components["schemas"]["ImageOutput"];
|
||||
img_channel_multiply: components["schemas"]["ImageOutput"];
|
||||
latents_collection: components["schemas"]["LatentsCollectionOutput"];
|
||||
midas_depth_image_processor: components["schemas"]["ImageOutput"];
|
||||
string_collection: components["schemas"]["StringCollectionOutput"];
|
||||
mask_from_id: components["schemas"]["ImageOutput"];
|
||||
string: components["schemas"]["StringOutput"];
|
||||
float: components["schemas"]["FloatOutput"];
|
||||
model_identifier: components["schemas"]["ModelIdentifierOutput"];
|
||||
pidi_image_processor: components["schemas"]["ImageOutput"];
|
||||
string_join: components["schemas"]["StringOutput"];
|
||||
spandrel_image_to_image_autoscale: components["schemas"]["ImageOutput"];
|
||||
lora_selector: components["schemas"]["LoRASelectorOutput"];
|
||||
clip_skip: components["schemas"]["CLIPSkipInvocationOutput"];
|
||||
image_mask_to_tensor: components["schemas"]["MaskOutput"];
|
||||
normalbae_image_processor: components["schemas"]["ImageOutput"];
|
||||
face_off: components["schemas"]["FaceOffOutput"];
|
||||
mul: components["schemas"]["IntegerOutput"];
|
||||
segment_anything_processor: components["schemas"]["ImageOutput"];
|
||||
round_float: components["schemas"]["FloatOutput"];
|
||||
sdxl_model_loader: components["schemas"]["SDXLModelLoaderOutput"];
|
||||
denoise_latents: components["schemas"]["LatentsOutput"];
|
||||
string_split_neg: components["schemas"]["StringPosNegOutput"];
|
||||
string_split: components["schemas"]["String2Output"];
|
||||
invert_tensor_mask: components["schemas"]["MaskOutput"];
|
||||
main_model_loader: components["schemas"]["ModelLoaderOutput"];
|
||||
img_crop: components["schemas"]["ImageOutput"];
|
||||
img_watermark: components["schemas"]["ImageOutput"];
|
||||
dw_openpose_image_processor: components["schemas"]["ImageOutput"];
|
||||
add: components["schemas"]["IntegerOutput"];
|
||||
conditioning: components["schemas"]["ConditioningOutput"];
|
||||
esrgan: components["schemas"]["ImageOutput"];
|
||||
t2i_adapter: components["schemas"]["T2IAdapterOutput"];
|
||||
sdxl_refiner_compel_prompt: components["schemas"]["ConditioningOutput"];
|
||||
mediapipe_face_processor: components["schemas"]["ImageOutput"];
|
||||
img_chan: components["schemas"]["ImageOutput"];
|
||||
face_mask_detection: components["schemas"]["FaceMaskOutput"];
|
||||
lineart_image_processor: components["schemas"]["ImageOutput"];
|
||||
blank_image: components["schemas"]["ImageOutput"];
|
||||
image_collection: components["schemas"]["ImageCollectionOutput"];
|
||||
img_nsfw: components["schemas"]["ImageOutput"];
|
||||
unsharp_mask: components["schemas"]["ImageOutput"];
|
||||
scheduler: components["schemas"]["SchedulerOutput"];
|
||||
metadata_item: components["schemas"]["MetadataItemOutput"];
|
||||
crop_latents: components["schemas"]["LatentsOutput"];
|
||||
string_join_three: components["schemas"]["StringOutput"];
|
||||
content_shuffle_image_processor: components["schemas"]["ImageOutput"];
|
||||
zoe_depth_image_processor: components["schemas"]["ImageOutput"];
|
||||
depth_anything_image_processor: components["schemas"]["ImageOutput"];
|
||||
controlnet: components["schemas"]["ControlOutput"];
|
||||
mask_edge: components["schemas"]["ImageOutput"];
|
||||
img_ilerp: components["schemas"]["ImageOutput"];
|
||||
lora_loader: components["schemas"]["LoRALoaderOutput"];
|
||||
calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"];
|
||||
face_identifier: components["schemas"]["ImageOutput"];
|
||||
i2l: components["schemas"]["LatentsOutput"];
|
||||
infill_lama: components["schemas"]["ImageOutput"];
|
||||
sdxl_lora_collection_loader: components["schemas"]["SDXLLoRALoaderOutput"];
|
||||
mask_combine: components["schemas"]["ImageOutput"];
|
||||
noise: components["schemas"]["NoiseOutput"];
|
||||
div: components["schemas"]["IntegerOutput"];
|
||||
img_paste: components["schemas"]["ImageOutput"];
|
||||
create_gradient_mask: components["schemas"]["GradientMaskOutput"];
|
||||
iterate: components["schemas"]["IterateInvocationOutput"];
|
||||
merge_tiles_to_image: components["schemas"]["ImageOutput"];
|
||||
l2i: components["schemas"]["ImageOutput"];
|
||||
float_math: components["schemas"]["FloatOutput"];
|
||||
img_lerp: components["schemas"]["ImageOutput"];
|
||||
spandrel_image_to_image: components["schemas"]["ImageOutput"];
|
||||
tiled_multi_diffusion_denoise_latents: components["schemas"]["LatentsOutput"];
|
||||
ip_adapter: components["schemas"]["IPAdapterOutput"];
|
||||
step_param_easing: components["schemas"]["FloatCollectionOutput"];
|
||||
heuristic_resize: components["schemas"]["ImageOutput"];
|
||||
canny_image_processor: components["schemas"]["ImageOutput"];
|
||||
hed_image_processor: components["schemas"]["ImageOutput"];
|
||||
img_mul: components["schemas"]["ImageOutput"];
|
||||
merge_metadata: components["schemas"]["MetadataOutput"];
|
||||
color: components["schemas"]["ColorOutput"];
|
||||
lscale: components["schemas"]["LatentsOutput"];
|
||||
integer_math: components["schemas"]["IntegerOutput"];
|
||||
infill_rgba: components["schemas"]["ImageOutput"];
|
||||
lineart_anime_image_processor: components["schemas"]["ImageOutput"];
|
||||
tile_image_processor: components["schemas"]["ImageOutput"];
|
||||
img_blur: components["schemas"]["ImageOutput"];
|
||||
float_to_int: components["schemas"]["IntegerOutput"];
|
||||
alpha_mask_to_tensor: components["schemas"]["MaskOutput"];
|
||||
collect: components["schemas"]["CollectInvocationOutput"];
|
||||
tile_to_properties: components["schemas"]["TileToPropertiesOutput"];
|
||||
infill_patchmatch: components["schemas"]["ImageOutput"];
|
||||
image: components["schemas"]["ImageOutput"];
|
||||
leres_image_processor: components["schemas"]["ImageOutput"];
|
||||
seamless: components["schemas"]["SeamlessModeOutput"];
|
||||
integer_collection: components["schemas"]["IntegerCollectionOutput"];
|
||||
iterate: components["schemas"]["IterateInvocationOutput"];
|
||||
img_scale: components["schemas"]["ImageOutput"];
|
||||
pair_tile_image: components["schemas"]["PairTileImageOutput"];
|
||||
canny_image_processor: components["schemas"]["ImageOutput"];
|
||||
integer_math: components["schemas"]["IntegerOutput"];
|
||||
leres_image_processor: components["schemas"]["ImageOutput"];
|
||||
clip_skip: components["schemas"]["CLIPSkipInvocationOutput"];
|
||||
float_collection: components["schemas"]["FloatCollectionOutput"];
|
||||
string_collection: components["schemas"]["StringCollectionOutput"];
|
||||
lora_collection_loader: components["schemas"]["LoRALoaderOutput"];
|
||||
mask_combine: components["schemas"]["ImageOutput"];
|
||||
latents_collection: components["schemas"]["LatentsCollectionOutput"];
|
||||
mask_edge: components["schemas"]["ImageOutput"];
|
||||
alpha_mask_to_tensor: components["schemas"]["MaskOutput"];
|
||||
img_conv: components["schemas"]["ImageOutput"];
|
||||
color_correct: components["schemas"]["ImageOutput"];
|
||||
lblend: components["schemas"]["LatentsOutput"];
|
||||
mask_from_id: components["schemas"]["ImageOutput"];
|
||||
img_pad_crop: components["schemas"]["ImageOutput"];
|
||||
metadata_item: components["schemas"]["MetadataItemOutput"];
|
||||
lora_loader: components["schemas"]["LoRALoaderOutput"];
|
||||
cv_inpaint: components["schemas"]["ImageOutput"];
|
||||
color_map_image_processor: components["schemas"]["ImageOutput"];
|
||||
img_resize: components["schemas"]["ImageOutput"];
|
||||
random_range: components["schemas"]["IntegerCollectionOutput"];
|
||||
img_crop: components["schemas"]["ImageOutput"];
|
||||
freeu: components["schemas"]["UNetOutput"];
|
||||
infill_tile: components["schemas"]["ImageOutput"];
|
||||
infill_rgba: components["schemas"]["ImageOutput"];
|
||||
tomask: components["schemas"]["ImageOutput"];
|
||||
sdxl_compel_prompt: components["schemas"]["ConditioningOutput"];
|
||||
sdxl_refiner_model_loader: components["schemas"]["SDXLRefinerModelLoaderOutput"];
|
||||
boolean: components["schemas"]["BooleanOutput"];
|
||||
core_metadata: components["schemas"]["MetadataOutput"];
|
||||
dynamic_prompt: components["schemas"]["StringCollectionOutput"];
|
||||
calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"];
|
||||
sdxl_lora_loader: components["schemas"]["SDXLLoRALoaderOutput"];
|
||||
canvas_paste_back: components["schemas"]["ImageOutput"];
|
||||
};
|
||||
/**
|
||||
* InvocationStartedEvent
|
||||
@ -12642,18 +12685,8 @@ export type components = {
|
||||
*/
|
||||
type: "string_split_neg";
|
||||
};
|
||||
/** StylePresetChanges */
|
||||
StylePresetChanges: {
|
||||
/**
|
||||
* Name
|
||||
* @description The style preset's new name.
|
||||
*/
|
||||
name?: string | null;
|
||||
/** @description The updated data for style preset. */
|
||||
preset_data?: components["schemas"]["PresetData"] | null;
|
||||
};
|
||||
/** StylePresetRecordDTO */
|
||||
StylePresetRecordDTO: {
|
||||
/** StylePresetRecordWithImage */
|
||||
StylePresetRecordWithImage: {
|
||||
/**
|
||||
* Name
|
||||
* @description The name of the style preset.
|
||||
@ -12671,16 +12704,11 @@ export type components = {
|
||||
* @description Whether or not the style preset is default
|
||||
*/
|
||||
is_default: boolean;
|
||||
};
|
||||
/** StylePresetWithoutId */
|
||||
StylePresetWithoutId: {
|
||||
/**
|
||||
* Name
|
||||
* @description The name of the style preset.
|
||||
* Image
|
||||
* @description The path for image
|
||||
*/
|
||||
name: string;
|
||||
/** @description The preset data */
|
||||
preset_data: components["schemas"]["PresetData"];
|
||||
image: string | null;
|
||||
};
|
||||
/**
|
||||
* SubModelType
|
||||
@ -16224,7 +16252,7 @@ export type operations = {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["StylePresetRecordDTO"];
|
||||
"application/json": components["schemas"]["StylePresetRecordWithImage"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
@ -16274,14 +16302,14 @@ export type operations = {
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["Body_update_style_preset"];
|
||||
"multipart/form-data": components["schemas"]["Body_update_style_preset"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["StylePresetRecordDTO"];
|
||||
"application/json": components["schemas"]["StylePresetRecordWithImage"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
@ -16301,7 +16329,7 @@ export type operations = {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["StylePresetRecordDTO"][];
|
||||
"application/json": components["schemas"]["StylePresetRecordWithImage"][];
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -16313,14 +16341,14 @@ export type operations = {
|
||||
create_style_preset: {
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["Body_create_style_preset"];
|
||||
"multipart/form-data": components["schemas"]["Body_create_style_preset"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["StylePresetRecordDTO"];
|
||||
"application/json": components["schemas"]["StylePresetRecordWithImage"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
@ -16331,4 +16359,38 @@ export type operations = {
|
||||
};
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Get Style Preset Image
|
||||
* @description Gets an image file that previews the model
|
||||
*/
|
||||
get_style_preset_image: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** @description The id of the style preset image to get */
|
||||
style_preset_id: string;
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description The style preset image was fetched successfully */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": unknown;
|
||||
};
|
||||
};
|
||||
/** @description Bad request */
|
||||
400: {
|
||||
content: never;
|
||||
};
|
||||
/** @description The style preset image could not be found */
|
||||
404: {
|
||||
content: never;
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
Loading…
Reference in New Issue
Block a user