diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx
index 079fc43a4a..cead80f362 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx
@@ -5,13 +5,21 @@ import {
isModalOpenChanged,
} from 'features/changeBoardModal/store/slice';
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
-import { useCallback } from 'react';
+import { useCallback, useMemo } from 'react';
import { FaFolder, FaTrash } from 'react-icons/fa';
+import { MdStar, MdStarBorder } from 'react-icons/md';
+import {
+ useStarImagesMutation,
+ useUnstarImagesMutation,
+} from '../../../../services/api/endpoints/images';
const MultipleSelectionMenuItems = () => {
const dispatch = useAppDispatch();
const selection = useAppSelector((state) => state.gallery.selection);
+ const [starImages] = useStarImagesMutation();
+ const [unstarImages] = useUnstarImagesMutation();
+
const handleChangeBoard = useCallback(() => {
dispatch(imagesToChangeSelected(selection));
dispatch(isModalOpenChanged(true));
@@ -21,8 +29,37 @@ const MultipleSelectionMenuItems = () => {
dispatch(imagesToDeleteSelected(selection));
}, [dispatch, selection]);
+ const handleStarSelection = useCallback(() => {
+ starImages({ images: selection });
+ }, [starImages, selection]);
+
+ const handleUnstarSelection = useCallback(() => {
+ unstarImages({ images: selection });
+ }, [unstarImages, selection]);
+
+ const areAllStarred = useMemo(() => {
+ return selection.every((img) => img.starred);
+ }, [selection]);
+
+ const areAllUnstarred = useMemo(() => {
+ return selection.every((img) => !img.starred);
+ }, [selection]);
+
return (
<>
+ {areAllStarred && (
+ }
+ onClickCapture={handleUnstarSelection}
+ >
+ Unstar All
+
+ )}
+ {areAllUnstarred && (
+ } onClickCapture={handleStarSelection}>
+ Star All
+
+ )}
} onClickCapture={handleChangeBoard}>
Change Board
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx
index cf87d670e3..b6100ca358 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx
@@ -30,8 +30,9 @@ import {
FaTrash,
} from 'react-icons/fa';
import {
- useChangeImagePinnedMutation,
useGetImageMetadataQuery,
+ useStarImagesMutation,
+ useUnstarImagesMutation,
} from 'services/api/endpoints/images';
import { ImageDTO } from 'services/api/types';
import { useDebounce } from 'use-debounce';
@@ -63,7 +64,8 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
: debouncedMetadataQueryArg ?? skipToken
);
- const [togglePin] = useChangeImagePinnedMutation();
+ const [starImages] = useStarImagesMutation();
+ const [unstarImages] = useUnstarImagesMutation();
const { isClipboardAPIAvailable, copyImageToClipboard } =
useCopyImageToClipboard();
@@ -133,13 +135,13 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
copyImageToClipboard(imageDTO.image_url);
}, [copyImageToClipboard, imageDTO.image_url]);
- const handlePinImage = useCallback(() => {
- togglePin({ imageDTO, pinned: true });
- }, [togglePin, imageDTO]);
+ const handleStarImage = useCallback(() => {
+ if (imageDTO) starImages({ images: [imageDTO] });
+ }, [starImages, imageDTO]);
- const handleUnpinImage = useCallback(() => {
- togglePin({ imageDTO, pinned: false });
- }, [togglePin, imageDTO]);
+ const handleUnstarImage = useCallback(() => {
+ if (imageDTO) unstarImages({ images: [imageDTO] });
+ }, [unstarImages, imageDTO]);
return (
<>
@@ -210,12 +212,12 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
} onClickCapture={handleChangeBoard}>
Change Board
- {imageDTO.pinned ? (
- } onClickCapture={handleUnpinImage}>
+ {imageDTO.starred ? (
+ } onClickCapture={handleUnstarImage}>
Unstar Image
) : (
- } onClickCapture={handlePinImage}>
+ } onClickCapture={handleStarImage}>
Star Image
)}
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx
index 9cd2c51a1b..9122563524 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx
@@ -1,4 +1,4 @@
-import { Box, Flex, useColorModeValue } from '@chakra-ui/react';
+import { Box, Flex } from '@chakra-ui/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIDndImage from 'common/components/IAIDndImage';
import IAIFillSkeleton from 'common/components/IAIFillSkeleton';
@@ -13,8 +13,9 @@ import { MouseEvent, memo, useCallback, useMemo, useState } from 'react';
import { FaTrash } from 'react-icons/fa';
import { MdStar, MdStarBorder } from 'react-icons/md';
import {
- useChangeImagePinnedMutation,
useGetImageDTOQuery,
+ useStarImagesMutation,
+ useUnstarImagesMutation,
} from 'services/api/endpoints/images';
import IAIDndImageIcon from '../../../../common/components/IAIDndImageIcon';
@@ -64,40 +65,26 @@ const GalleryImage = (props: HoverableImageProps) => {
}
}, [imageDTO, selection, selectionCount]);
- const [togglePin] = useChangeImagePinnedMutation();
+ const [starImages] = useStarImagesMutation();
+ const [unstarImages] = useUnstarImagesMutation();
- const togglePinnedState = useCallback(() => {
+ const toggleStarredState = useCallback(() => {
if (imageDTO) {
- togglePin({ imageDTO, pinned: !imageDTO.pinned });
+ if (imageDTO.starred) {
+ unstarImages({ images: [imageDTO] });
+ }
+ if (!imageDTO.starred) {
+ starImages({ images: [imageDTO] });
+ }
}
- }, [togglePin, imageDTO]);
+ }, [starImages, unstarImages, imageDTO]);
const [isHovered, setIsHovered] = useState(false);
- const pinIcon = useMemo(() => {
- if (imageDTO?.pinned) return ;
- if (!imageDTO?.pinned && isHovered) return ;
- }, [imageDTO?.pinned, isHovered]);
-
- const resetIconShadow = useColorModeValue(
- `drop-shadow(0px 0px 0.1rem var(--invokeai-colors-base-600))`,
- `drop-shadow(0px 0px 0.1rem var(--invokeai-colors-base-800))`
- );
-
- const iconButtonStyles = {
- position: 'absolute',
- top: 1,
- insetInlineEnd: 1,
- p: 0,
- minW: 0,
- svg: {
- transitionProperty: 'common',
- transitionDuration: 'normal',
- fill: 'base.100',
- _hover: { fill: 'base.50' },
- filter: resetIconShadow,
- },
- };
+ const starIcon = useMemo(() => {
+ if (imageDTO?.starred) return ;
+ if (!imageDTO?.starred && isHovered) return ;
+ }, [imageDTO?.starred, isHovered]);
if (!imageDTO) {
return ;
@@ -130,9 +117,9 @@ const GalleryImage = (props: HoverableImageProps) => {
>
<>
{isHovered && shouldShowDeleteButton && (
diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.ts b/invokeai/frontend/web/src/services/api/endpoints/images.ts
index ef99d735c2..e697badad2 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/images.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/images.ts
@@ -388,112 +388,217 @@ export const imagesApi = api.injectEndpoints({
},
}),
/**
- * Change an image's `pinned` state.
+ * Star a list of images.
*/
- changeImagePinned: build.mutation<
- ImageDTO,
- { imageDTO: ImageDTO; pinned: boolean }
+ starImages: build.mutation<
+ void,
+ { images: ImageDTO[] }
>({
- query: ({ imageDTO, pinned }) => ({
- url: `images/i/${imageDTO.image_name}`,
- method: 'PATCH',
- body: { pinned },
+ query: ({ images }) => ({
+ url: `images/star`,
+ method: 'POST',
+ body: { image_names: images.map(img => img.image_name) },
}),
- invalidatesTags: (result, error, { imageDTO }) => {
- const categories = getCategories(imageDTO);
- return [
- {
- type: 'ImageList',
- id: getListImagesUrl({
- board_id: imageDTO.board_id,
- categories,
- }),
- },
- ]
+ invalidatesTags: (result, error, { images }) => {
+ // assume all images are on the same board/category
+ if (images[0]) {
+ const categories = getCategories(images[0]);
+ const boardId = images[0].board_id;
+ return [
+ {
+ type: 'ImageList',
+ id: getListImagesUrl({
+ board_id: boardId,
+ categories,
+ }),
+ },
+ ]
+ }
+ return []
+
},
async onQueryStarted(
- { imageDTO, pinned },
+ { images },
{ dispatch, queryFulfilled, getState }
) {
- /**
- * Cache changes for `changeImagePinned`:
- * - *update* getImageDTO
- */
+ try {
+ /**
+ * Cache changes for pinImages:
+ * - *update* getImageDTO for each image
+ * - *upsert* into list for each image
+ */
- // Store patches so we can undo if the query fails
- const patches: PatchCollection[] = [];
+ // assume all images are on the same board/category
+ if (!images[0]) return;
+ const categories = getCategories(images[0]);
+ const boardId = images[0].board_id;
- // *update* getImageDTO
- patches.push(
- dispatch(
- imagesApi.util.updateQueryData(
- 'getImageDTO',
- imageDTO.image_name,
- (draft) => {
- Object.assign(draft, { pinned });
- }
- )
- )
- );
-
- const categories = getCategories(imageDTO);
-
- const queryArgs = {
- board_id: imageDTO.board_id ?? 'none',
- categories,
- };
-
- const currentCache = imagesApi.endpoints.listImages.select(queryArgs)(
- getState()
- );
-
- const { data: total } = IMAGE_CATEGORIES.includes(
- imageDTO.image_category
- )
- ? boardsApi.endpoints.getBoardImagesTotal.select(
- imageDTO.board_id ?? 'none'
- )(getState())
- : boardsApi.endpoints.getBoardAssetsTotal.select(
- imageDTO.board_id ?? 'none'
- )(getState());
-
- // IF it eligible for insertion into existing $cache
- // "eligible" means either:
- // - The cache is fully populated, with all images in the db cached
- // OR
- // - The image's `created_at` is within the range of the cached images within that pinned state
-
- const updatedImage: ImageDTO = { ...imageDTO, pinned }
-
- const isCacheFullyPopulated =
- currentCache.data && currentCache.data.ids.length >= (total ?? 0);
-
- const isInDateRange = getIsImageInDateRange(
- currentCache.data,
- updatedImage
- );
-
- // should we remove images from cache if _not_ in date range? ie you are showing 100 of 101 pinned images and you unpin one. technically it should disappear from list.
-
- if (isCacheFullyPopulated || isInDateRange) {
- // *upsert* to $cache
- patches.push(
+ images.forEach((imageDTO) => {
+ const { image_name } = imageDTO;
dispatch(
imagesApi.util.updateQueryData(
- 'listImages',
- queryArgs,
+ 'getImageDTO',
+ image_name,
(draft) => {
- imagesAdapter.upsertOne(draft, updatedImage);
+ draft.starred = true;
}
)
- )
- );
- }
+ );
- try {
- await queryFulfilled;
+ const queryArgs = {
+ board_id: boardId ?? 'none',
+ categories
+ };
+
+ const currentCache = imagesApi.endpoints.listImages.select(
+ queryArgs
+ )(getState());
+
+ const { data: previousTotal } = IMAGE_CATEGORIES.includes(
+ imageDTO.image_category
+ )
+ ? boardsApi.endpoints.getBoardImagesTotal.select(
+ boardId ?? 'none'
+ )(getState())
+ : boardsApi.endpoints.getBoardAssetsTotal.select(
+ boardId ?? 'none'
+ )(getState());
+
+ const isCacheFullyPopulated =
+ currentCache.data &&
+ currentCache.data.ids.length >= (previousTotal ?? 0);
+
+ const isInDateRange =
+ (previousTotal || 0) >= IMAGE_LIMIT
+ ? getIsImageInDateRange(currentCache.data, imageDTO)
+ : true;
+
+ if (isCacheFullyPopulated || isInDateRange) {
+ // *upsert* to $cache
+ dispatch(
+ imagesApi.util.updateQueryData(
+ 'listImages',
+ queryArgs,
+ (draft) => {
+ imagesAdapter.upsertOne(draft, {
+ ...imageDTO,
+ starred: true
+ });
+ }
+ )
+ );
+ }
+ });
} catch {
- patches.forEach((patchResult) => patchResult.undo());
+ // no-op
+ }
+ },
+ }),
+ /**
+ * Unstar a list of images.
+ */
+ unstarImages: build.mutation<
+ void,
+ { images: ImageDTO[] }
+ >({
+ query: ({ images }) => ({
+ url: `images/unstar`,
+ method: 'POST',
+ body: { image_names: images.map(img => img.image_name) },
+ }),
+ invalidatesTags: (result, error, { images }) => {
+ // assume all images are on the same board/category
+ if (images[0]) {
+ const categories = getCategories(images[0]);
+ const boardId = images[0].board_id;
+ return [
+ {
+ type: 'ImageList',
+ id: getListImagesUrl({
+ board_id: boardId,
+ categories,
+ }),
+ },
+ ]
+ }
+ return []
+
+ },
+ async onQueryStarted(
+ { images },
+ { dispatch, queryFulfilled, getState }
+ ) {
+ try {
+ /**
+ * Cache changes for unstarImages:
+ * - *update* getImageDTO for each image
+ * - *upsert* into list for each image
+ */
+
+ // assume all images are on the same board/category
+ if (!images[0]) return;
+ const categories = getCategories(images[0]);
+ const boardId = images[0].board_id;
+
+ images.forEach((imageDTO) => {
+ const { image_name } = imageDTO;
+ dispatch(
+ imagesApi.util.updateQueryData(
+ 'getImageDTO',
+ image_name,
+ (draft) => {
+ draft.starred = false;
+ }
+ )
+ );
+
+
+ const queryArgs = {
+ board_id: boardId ?? 'none',
+ categories
+ };
+
+ const currentCache = imagesApi.endpoints.listImages.select(
+ queryArgs
+ )(getState());
+
+ const { data: previousTotal } = IMAGE_CATEGORIES.includes(
+ imageDTO.image_category
+ )
+ ? boardsApi.endpoints.getBoardImagesTotal.select(
+ boardId ?? 'none'
+ )(getState())
+ : boardsApi.endpoints.getBoardAssetsTotal.select(
+ boardId ?? 'none'
+ )(getState());
+
+ const isCacheFullyPopulated =
+ currentCache.data &&
+ currentCache.data.ids.length >= (previousTotal ?? 0);
+
+ const isInDateRange =
+ (previousTotal || 0) >= IMAGE_LIMIT
+ ? getIsImageInDateRange(currentCache.data, imageDTO)
+ : true;
+
+ if (isCacheFullyPopulated || isInDateRange) {
+ // *upsert* to $cache
+ dispatch(
+ imagesApi.util.updateQueryData(
+ 'listImages',
+ queryArgs,
+ (draft) => {
+ imagesAdapter.upsertOne(draft, {
+ ...imageDTO,
+ starred: false
+ });
+ }
+ )
+ );
+ }
+ });
+ } catch {
+ // no-op
}
},
}),
@@ -1268,7 +1373,8 @@ export const {
useRemoveImageFromBoardMutation,
useChangeImageIsIntermediateMutation,
useChangeImageSessionIdMutation,
- useChangeImagePinnedMutation,
useDeleteBoardAndImagesMutation,
useDeleteBoardMutation,
+ useStarImagesMutation,
+ useUnstarImagesMutation
} = imagesApi;
diff --git a/invokeai/frontend/web/src/services/api/schema.d.ts b/invokeai/frontend/web/src/services/api/schema.d.ts
index ea40ea4f6f..bac9f1ae96 100644
--- a/invokeai/frontend/web/src/services/api/schema.d.ts
+++ b/invokeai/frontend/web/src/services/api/schema.d.ts
@@ -209,6 +209,14 @@ export type paths = {
/** Delete Images From List */
post: operations['delete_images_from_list'];
};
+ '/api/v1/images/star': {
+ /** Star Images In List */
+ post: operations['star_images_in_list'];
+ };
+ '/api/v1/images/unstar': {
+ /** Unstar Images In List */
+ post: operations['unstar_images_in_list'];
+ };
'/api/v1/boards/': {
/**
* List Boards
@@ -539,6 +547,22 @@ export type components = {
*/
image_names: string[];
};
+ /** Body_star_images_in_list */
+ Body_star_images_in_list: {
+ /**
+ * Image Names
+ * @description The list of names of images to star
+ */
+ image_names: string[];
+ };
+ /** Body_unstar_images_in_list */
+ Body_unstar_images_in_list: {
+ /**
+ * Image Names
+ * @description The list of names of images to unstar
+ */
+ image_names: string[];
+ };
/** Body_upload_image */
Body_upload_image: {
/**
@@ -1927,21 +1951,6 @@ export type components = {
nodes?: {
[key: string]:
| (
- | components['schemas']['BooleanInvocation']
- | components['schemas']['BooleanCollectionInvocation']
- | components['schemas']['IntegerInvocation']
- | components['schemas']['IntegerCollectionInvocation']
- | components['schemas']['FloatInvocation']
- | components['schemas']['FloatCollectionInvocation']
- | components['schemas']['StringInvocation']
- | components['schemas']['StringCollectionInvocation']
- | components['schemas']['ImageInvocation']
- | components['schemas']['ImageCollectionInvocation']
- | components['schemas']['LatentsInvocation']
- | components['schemas']['LatentsCollectionInvocation']
- | components['schemas']['ColorInvocation']
- | components['schemas']['ConditioningInvocation']
- | components['schemas']['ConditioningCollectionInvocation']
| components['schemas']['ControlNetInvocation']
| components['schemas']['ImageProcessorInvocation']
| components['schemas']['MainModelLoaderInvocation']
@@ -1949,21 +1958,15 @@ export type components = {
| components['schemas']['SDXLLoraLoaderInvocation']
| components['schemas']['VaeLoaderInvocation']
| components['schemas']['MetadataAccumulatorInvocation']
- | components['schemas']['SDXLModelLoaderInvocation']
- | components['schemas']['SDXLRefinerModelLoaderInvocation']
+ | components['schemas']['RangeInvocation']
+ | components['schemas']['RangeOfSizeInvocation']
+ | components['schemas']['RandomRangeInvocation']
+ | components['schemas']['ImageCollectionInvocation']
| components['schemas']['CompelInvocation']
| components['schemas']['SDXLCompelPromptInvocation']
| components['schemas']['SDXLRefinerCompelPromptInvocation']
| components['schemas']['ClipSkipInvocation']
- | components['schemas']['DenoiseLatentsInvocation']
- | components['schemas']['LatentsToImageInvocation']
- | components['schemas']['ResizeLatentsInvocation']
- | components['schemas']['ScaleLatentsInvocation']
- | components['schemas']['ImageToLatentsInvocation']
- | components['schemas']['ONNXPromptInvocation']
- | components['schemas']['ONNXTextToLatentsInvocation']
- | components['schemas']['ONNXLatentsToImageInvocation']
- | components['schemas']['OnnxModelLoaderInvocation']
+ | components['schemas']['LoadImageInvocation']
| components['schemas']['ShowImageInvocation']
| components['schemas']['ImageCropInvocation']
| components['schemas']['ImagePasteInvocation']
@@ -1984,24 +1987,37 @@ export type components = {
| components['schemas']['ImageHueAdjustmentInvocation']
| components['schemas']['ImageLuminosityAdjustmentInvocation']
| components['schemas']['ImageSaturationAdjustmentInvocation']
- | components['schemas']['DynamicPromptInvocation']
- | components['schemas']['PromptsFromFileInvocation']
| components['schemas']['CvInpaintInvocation']
- | components['schemas']['FloatLinearRangeInvocation']
- | components['schemas']['StepParamEasingInvocation']
+ | components['schemas']['InfillColorInvocation']
+ | components['schemas']['InfillTileInvocation']
+ | components['schemas']['InfillPatchMatchInvocation']
+ | components['schemas']['DenoiseLatentsInvocation']
+ | components['schemas']['LatentsToImageInvocation']
+ | components['schemas']['ResizeLatentsInvocation']
+ | components['schemas']['ScaleLatentsInvocation']
+ | components['schemas']['ImageToLatentsInvocation']
| components['schemas']['AddInvocation']
| components['schemas']['SubtractInvocation']
| components['schemas']['MultiplyInvocation']
| components['schemas']['DivideInvocation']
| components['schemas']['RandomIntInvocation']
| components['schemas']['NoiseInvocation']
- | components['schemas']['RangeInvocation']
- | components['schemas']['RangeOfSizeInvocation']
- | components['schemas']['RandomRangeInvocation']
+ | components['schemas']['ONNXPromptInvocation']
+ | components['schemas']['ONNXTextToLatentsInvocation']
+ | components['schemas']['ONNXLatentsToImageInvocation']
+ | components['schemas']['ONNXSD1ModelLoaderInvocation']
+ | components['schemas']['OnnxModelLoaderInvocation']
+ | components['schemas']['DynamicPromptInvocation']
+ | components['schemas']['PromptsFromFileInvocation']
+ | components['schemas']['ParamIntInvocation']
+ | components['schemas']['ParamFloatInvocation']
+ | components['schemas']['ParamStringInvocation']
+ | components['schemas']['ParamPromptInvocation']
+ | components['schemas']['FloatLinearRangeInvocation']
+ | components['schemas']['StepParamEasingInvocation']
+ | components['schemas']['SDXLModelLoaderInvocation']
+ | components['schemas']['SDXLRefinerModelLoaderInvocation']
| components['schemas']['ESRGANInvocation']
- | components['schemas']['InfillColorInvocation']
- | components['schemas']['InfillTileInvocation']
- | components['schemas']['InfillPatchMatchInvocation']
| components['schemas']['GraphInvocation']
| components['schemas']['IterateInvocation']
| components['schemas']['CollectInvocation']
@@ -2507,10 +2523,10 @@ export type components = {
*/
node_id?: string;
/**
- * Pinned
- * @description Whether this image is pinned.
+ * Starred
+ * @description Whether this image is starred.
*/
- pinned: boolean;
+ starred: boolean;
/**
* Board Id
* @description The id of the board the image belongs to, if one exists.
@@ -2899,7 +2915,7 @@ export type components = {
* - `image_category`: change the category of an image
* - `session_id`: change the session associated with an image
* - `is_intermediate`: change the image's `is_intermediate` flag
- * - `pinned`: change whether the image is pinned
+ * - `starred`: change whether the image is starred
*/
ImageRecordChanges: {
/** @description The image's new category. */
@@ -2915,10 +2931,10 @@ export type components = {
*/
is_intermediate?: boolean;
/**
- * Pinned
- * @description The image's new `pinned` state
+ * Starred
+ * @description The image's new `starred` state
*/
- pinned?: boolean;
+ starred?: boolean;
};
/**
* Resize Image
@@ -6390,36 +6406,20 @@ export type components = {
image?: components['schemas']['ImageField'];
};
/**
- * UIConfigBase
- * @description Provides additional node configuration to the UI.
- * This is used internally by the @tags and @title decorator logic. You probably want to use those
- * decorators, though you may add this class to a node definition to specify the title and tags.
- */
- UIConfigBase: {
- /**
- * Tags
- * @description The tags to display in the UI
- */
- tags?: string[];
- /**
- * Title
- * @description The display name of the node
- */
- title?: string;
- };
- /**
- * Input
- * @description The type of input a field accepts.
- * - `Input.Direct`: The field must have its value provided directly, when the invocation and field are instantiated.
- * - `Input.Connection`: The field must have its value provided by a connection.
- * - `Input.Any`: The field may have its value provided either directly or by a connection.
+ * StableDiffusion2ModelFormat
+ * @description An enumeration.
* @enum {string}
*/
Input: 'connection' | 'direct' | 'any';
/**
- * UIType
- * @description Type hints for the UI.
- * If a field should be provided a data type that does not exactly match the python type of the field, use this to provide the type that should be used instead. See the node development docs for detail on adding a new field type, which involves client-side changes.
+ * ControlNetModelFormat
+ * @description An enumeration.
+ * @enum {string}
+ */
+ ControlNetModelFormat: 'checkpoint' | 'diffusers';
+ /**
+ * StableDiffusion1ModelFormat
+ * @description An enumeration.
* @enum {string}
*/
UIType:
@@ -6456,11 +6456,11 @@ export type components = {
| 'FilePath'
| 'enum';
/**
- * UIComponent
- * @description The type of UI component to use for a field, used to override the default components, which are inferred from the field type.
+ * StableDiffusionOnnxModelFormat
+ * @description An enumeration.
* @enum {string}
*/
- UIComponent: 'none' | 'textarea' | 'slider';
+ StableDiffusionOnnxModelFormat: 'olive' | 'onnx';
/**
* _InputField
* @description *DO NOT USE*
@@ -6627,21 +6627,6 @@ export type operations = {
requestBody: {
content: {
'application/json':
- | components['schemas']['BooleanInvocation']
- | components['schemas']['BooleanCollectionInvocation']
- | components['schemas']['IntegerInvocation']
- | components['schemas']['IntegerCollectionInvocation']
- | components['schemas']['FloatInvocation']
- | components['schemas']['FloatCollectionInvocation']
- | components['schemas']['StringInvocation']
- | components['schemas']['StringCollectionInvocation']
- | components['schemas']['ImageInvocation']
- | components['schemas']['ImageCollectionInvocation']
- | components['schemas']['LatentsInvocation']
- | components['schemas']['LatentsCollectionInvocation']
- | components['schemas']['ColorInvocation']
- | components['schemas']['ConditioningInvocation']
- | components['schemas']['ConditioningCollectionInvocation']
| components['schemas']['ControlNetInvocation']
| components['schemas']['ImageProcessorInvocation']
| components['schemas']['MainModelLoaderInvocation']
@@ -6649,21 +6634,15 @@ export type operations = {
| components['schemas']['SDXLLoraLoaderInvocation']
| components['schemas']['VaeLoaderInvocation']
| components['schemas']['MetadataAccumulatorInvocation']
- | components['schemas']['SDXLModelLoaderInvocation']
- | components['schemas']['SDXLRefinerModelLoaderInvocation']
+ | components['schemas']['RangeInvocation']
+ | components['schemas']['RangeOfSizeInvocation']
+ | components['schemas']['RandomRangeInvocation']
+ | components['schemas']['ImageCollectionInvocation']
| components['schemas']['CompelInvocation']
| components['schemas']['SDXLCompelPromptInvocation']
| components['schemas']['SDXLRefinerCompelPromptInvocation']
| components['schemas']['ClipSkipInvocation']
- | components['schemas']['DenoiseLatentsInvocation']
- | components['schemas']['LatentsToImageInvocation']
- | components['schemas']['ResizeLatentsInvocation']
- | components['schemas']['ScaleLatentsInvocation']
- | components['schemas']['ImageToLatentsInvocation']
- | components['schemas']['ONNXPromptInvocation']
- | components['schemas']['ONNXTextToLatentsInvocation']
- | components['schemas']['ONNXLatentsToImageInvocation']
- | components['schemas']['OnnxModelLoaderInvocation']
+ | components['schemas']['LoadImageInvocation']
| components['schemas']['ShowImageInvocation']
| components['schemas']['ImageCropInvocation']
| components['schemas']['ImagePasteInvocation']
@@ -6684,24 +6663,37 @@ export type operations = {
| components['schemas']['ImageHueAdjustmentInvocation']
| components['schemas']['ImageLuminosityAdjustmentInvocation']
| components['schemas']['ImageSaturationAdjustmentInvocation']
- | components['schemas']['DynamicPromptInvocation']
- | components['schemas']['PromptsFromFileInvocation']
| components['schemas']['CvInpaintInvocation']
- | components['schemas']['FloatLinearRangeInvocation']
- | components['schemas']['StepParamEasingInvocation']
+ | components['schemas']['InfillColorInvocation']
+ | components['schemas']['InfillTileInvocation']
+ | components['schemas']['InfillPatchMatchInvocation']
+ | components['schemas']['DenoiseLatentsInvocation']
+ | components['schemas']['LatentsToImageInvocation']
+ | components['schemas']['ResizeLatentsInvocation']
+ | components['schemas']['ScaleLatentsInvocation']
+ | components['schemas']['ImageToLatentsInvocation']
| components['schemas']['AddInvocation']
| components['schemas']['SubtractInvocation']
| components['schemas']['MultiplyInvocation']
| components['schemas']['DivideInvocation']
| components['schemas']['RandomIntInvocation']
| components['schemas']['NoiseInvocation']
- | components['schemas']['RangeInvocation']
- | components['schemas']['RangeOfSizeInvocation']
- | components['schemas']['RandomRangeInvocation']
+ | components['schemas']['ONNXPromptInvocation']
+ | components['schemas']['ONNXTextToLatentsInvocation']
+ | components['schemas']['ONNXLatentsToImageInvocation']
+ | components['schemas']['ONNXSD1ModelLoaderInvocation']
+ | components['schemas']['OnnxModelLoaderInvocation']
+ | components['schemas']['DynamicPromptInvocation']
+ | components['schemas']['PromptsFromFileInvocation']
+ | components['schemas']['ParamIntInvocation']
+ | components['schemas']['ParamFloatInvocation']
+ | components['schemas']['ParamStringInvocation']
+ | components['schemas']['ParamPromptInvocation']
+ | components['schemas']['FloatLinearRangeInvocation']
+ | components['schemas']['StepParamEasingInvocation']
+ | components['schemas']['SDXLModelLoaderInvocation']
+ | components['schemas']['SDXLRefinerModelLoaderInvocation']
| components['schemas']['ESRGANInvocation']
- | components['schemas']['InfillColorInvocation']
- | components['schemas']['InfillTileInvocation']
- | components['schemas']['InfillPatchMatchInvocation']
| components['schemas']['GraphInvocation']
| components['schemas']['IterateInvocation']
| components['schemas']['CollectInvocation']
@@ -6757,21 +6749,6 @@ export type operations = {
requestBody: {
content: {
'application/json':
- | components['schemas']['BooleanInvocation']
- | components['schemas']['BooleanCollectionInvocation']
- | components['schemas']['IntegerInvocation']
- | components['schemas']['IntegerCollectionInvocation']
- | components['schemas']['FloatInvocation']
- | components['schemas']['FloatCollectionInvocation']
- | components['schemas']['StringInvocation']
- | components['schemas']['StringCollectionInvocation']
- | components['schemas']['ImageInvocation']
- | components['schemas']['ImageCollectionInvocation']
- | components['schemas']['LatentsInvocation']
- | components['schemas']['LatentsCollectionInvocation']
- | components['schemas']['ColorInvocation']
- | components['schemas']['ConditioningInvocation']
- | components['schemas']['ConditioningCollectionInvocation']
| components['schemas']['ControlNetInvocation']
| components['schemas']['ImageProcessorInvocation']
| components['schemas']['MainModelLoaderInvocation']
@@ -6779,21 +6756,15 @@ export type operations = {
| components['schemas']['SDXLLoraLoaderInvocation']
| components['schemas']['VaeLoaderInvocation']
| components['schemas']['MetadataAccumulatorInvocation']
- | components['schemas']['SDXLModelLoaderInvocation']
- | components['schemas']['SDXLRefinerModelLoaderInvocation']
+ | components['schemas']['RangeInvocation']
+ | components['schemas']['RangeOfSizeInvocation']
+ | components['schemas']['RandomRangeInvocation']
+ | components['schemas']['ImageCollectionInvocation']
| components['schemas']['CompelInvocation']
| components['schemas']['SDXLCompelPromptInvocation']
| components['schemas']['SDXLRefinerCompelPromptInvocation']
| components['schemas']['ClipSkipInvocation']
- | components['schemas']['DenoiseLatentsInvocation']
- | components['schemas']['LatentsToImageInvocation']
- | components['schemas']['ResizeLatentsInvocation']
- | components['schemas']['ScaleLatentsInvocation']
- | components['schemas']['ImageToLatentsInvocation']
- | components['schemas']['ONNXPromptInvocation']
- | components['schemas']['ONNXTextToLatentsInvocation']
- | components['schemas']['ONNXLatentsToImageInvocation']
- | components['schemas']['OnnxModelLoaderInvocation']
+ | components['schemas']['LoadImageInvocation']
| components['schemas']['ShowImageInvocation']
| components['schemas']['ImageCropInvocation']
| components['schemas']['ImagePasteInvocation']
@@ -6814,24 +6785,37 @@ export type operations = {
| components['schemas']['ImageHueAdjustmentInvocation']
| components['schemas']['ImageLuminosityAdjustmentInvocation']
| components['schemas']['ImageSaturationAdjustmentInvocation']
- | components['schemas']['DynamicPromptInvocation']
- | components['schemas']['PromptsFromFileInvocation']
| components['schemas']['CvInpaintInvocation']
- | components['schemas']['FloatLinearRangeInvocation']
- | components['schemas']['StepParamEasingInvocation']
+ | components['schemas']['InfillColorInvocation']
+ | components['schemas']['InfillTileInvocation']
+ | components['schemas']['InfillPatchMatchInvocation']
+ | components['schemas']['DenoiseLatentsInvocation']
+ | components['schemas']['LatentsToImageInvocation']
+ | components['schemas']['ResizeLatentsInvocation']
+ | components['schemas']['ScaleLatentsInvocation']
+ | components['schemas']['ImageToLatentsInvocation']
| components['schemas']['AddInvocation']
| components['schemas']['SubtractInvocation']
| components['schemas']['MultiplyInvocation']
| components['schemas']['DivideInvocation']
| components['schemas']['RandomIntInvocation']
| components['schemas']['NoiseInvocation']
- | components['schemas']['RangeInvocation']
- | components['schemas']['RangeOfSizeInvocation']
- | components['schemas']['RandomRangeInvocation']
+ | components['schemas']['ONNXPromptInvocation']
+ | components['schemas']['ONNXTextToLatentsInvocation']
+ | components['schemas']['ONNXLatentsToImageInvocation']
+ | components['schemas']['ONNXSD1ModelLoaderInvocation']
+ | components['schemas']['OnnxModelLoaderInvocation']
+ | components['schemas']['DynamicPromptInvocation']
+ | components['schemas']['PromptsFromFileInvocation']
+ | components['schemas']['ParamIntInvocation']
+ | components['schemas']['ParamFloatInvocation']
+ | components['schemas']['ParamStringInvocation']
+ | components['schemas']['ParamPromptInvocation']
+ | components['schemas']['FloatLinearRangeInvocation']
+ | components['schemas']['StepParamEasingInvocation']
+ | components['schemas']['SDXLModelLoaderInvocation']
+ | components['schemas']['SDXLRefinerModelLoaderInvocation']
| components['schemas']['ESRGANInvocation']
- | components['schemas']['InfillColorInvocation']
- | components['schemas']['InfillTileInvocation']
- | components['schemas']['InfillPatchMatchInvocation']
| components['schemas']['GraphInvocation']
| components['schemas']['IterateInvocation']
| components['schemas']['CollectInvocation']
@@ -7723,6 +7707,50 @@ export type operations = {
};
};
};
+ /** Star Images In List */
+ star_images_in_list: {
+ requestBody: {
+ content: {
+ 'application/json': components['schemas']['Body_star_images_in_list'];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ content: {
+ 'application/json': unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ content: {
+ 'application/json': components['schemas']['HTTPValidationError'];
+ };
+ };
+ };
+ };
+ /** Unstar Images In List */
+ unstar_images_in_list: {
+ requestBody: {
+ content: {
+ 'application/json': components['schemas']['Body_unstar_images_in_list'];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ content: {
+ 'application/json': unknown;
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ content: {
+ 'application/json': components['schemas']['HTTPValidationError'];
+ };
+ };
+ };
+ };
/**
* List Boards
* @description Gets a list of boards
diff --git a/invokeai/frontend/web/src/services/api/util.ts b/invokeai/frontend/web/src/services/api/util.ts
index a97e96f312..95f2b10653 100644
--- a/invokeai/frontend/web/src/services/api/util.ts
+++ b/invokeai/frontend/web/src/services/api/util.ts
@@ -21,32 +21,28 @@ export const getIsImageInDateRange = (
return true;
}
- const cachedPinnedImages = [];
- const cachedUnpinnedImages = [];
+ const cachedStarredImages = [];
+ const cachedUnstarredImages = [];
for (let index = 0; index < totalCachedImageDtos.length; index++) {
const image = totalCachedImageDtos[index];
- if (image?.pinned) cachedPinnedImages.push(image)
- if (!image?.pinned) cachedUnpinnedImages.push(image)
+ if (image?.starred) cachedStarredImages.push(image)
+ if (!image?.starred) cachedUnstarredImages.push(image)
}
- const lastPinnedImage = cachedPinnedImages[cachedPinnedImages.length - 1];
- const lastUnpinnedImage = cachedUnpinnedImages[cachedUnpinnedImages.length - 1];
-
- if (!lastPinnedImage || !lastUnpinnedImage) {
- // satisfy TS gods, we already confirmed the array has more than one image
- return false;
- }
-
- if (imageDTO.pinned) {
- // if pinning or already pinned, want to look in list of pinned images
+ if (imageDTO.starred) {
+ const lastStarredImage = cachedStarredImages[cachedStarredImages.length - 1];
+ // if starring or already starred, want to look in list of starred images
+ if (!lastStarredImage) return true; // no starred images showing, so always show this one
const createdDate = new Date(imageDTO.created_at);
- const oldestDate = new Date(lastPinnedImage.created_at);
+ const oldestDate = new Date(lastStarredImage.created_at);
return createdDate >= oldestDate;
} else {
- // if unpinning or already unpinned, want to look in list of unpinned images
+ const lastUnstarredImage = cachedUnstarredImages[cachedUnstarredImages.length - 1];
+ // if unstarring or already unstarred, want to look in list of unstarred images
+ if (!lastUnstarredImage) return false; // no unstarred images showing, so don't show this one
const createdDate = new Date(imageDTO.created_at);
- const oldestDate = new Date(lastUnpinnedImage.created_at);
+ const oldestDate = new Date(lastUnstarredImage.created_at);
return createdDate >= oldestDate;
}
@@ -64,11 +60,11 @@ export const getCategories = (imageDTO: ImageDTO) => {
export const imagesAdapter = createEntityAdapter({
selectId: (image) => image.image_name,
sortComparer: (a, b) => {
- // Compare pinned images first
- if (a.pinned && !b.pinned) {
+ // Compare starred images first
+ if (a.starred && !b.starred) {
return -1;
}
- if (!a.pinned && b.pinned) {
+ if (!a.starred && b.starred) {
return 1;
}
return dateComparator(b.created_at, a.created_at)