mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): resolve all typescript issues
This commit is contained in:
parent
75863e7181
commit
5468d9a9fc
@ -27,7 +27,7 @@ const STYLES: ChakraProps['sx'] = {
|
||||
|
||||
const DragPreview = (props: OverlayDragImageProps) => {
|
||||
if (!props.dragData) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (props.dragData.payloadType === 'IMAGE_DTO') {
|
||||
|
@ -1,23 +0,0 @@
|
||||
import { Box, forwardRef, Icon } from '@chakra-ui/react';
|
||||
import { Feature } from 'app/features';
|
||||
import { memo } from 'react';
|
||||
import { IconType } from 'react-icons';
|
||||
import { MdHelp } from 'react-icons/md';
|
||||
import GuidePopover from './GuidePopover';
|
||||
|
||||
type GuideIconProps = {
|
||||
feature: Feature;
|
||||
icon?: IconType;
|
||||
};
|
||||
|
||||
const GuideIcon = forwardRef(
|
||||
({ feature, icon = MdHelp }: GuideIconProps, ref) => (
|
||||
<GuidePopover feature={feature}>
|
||||
<Box ref={ref}>
|
||||
<Icon marginBottom="-.15rem" as={icon} />
|
||||
</Box>
|
||||
</GuidePopover>
|
||||
)
|
||||
);
|
||||
|
||||
export default memo(GuideIcon);
|
@ -1,49 +0,0 @@
|
||||
import {
|
||||
Box,
|
||||
Popover,
|
||||
PopoverArrow,
|
||||
PopoverBody,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { Feature, useFeatureHelpInfo } from 'app/features';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import { SystemState } from 'features/system/store/systemSlice';
|
||||
import { memo, ReactElement } from 'react';
|
||||
|
||||
type GuideProps = {
|
||||
children: ReactElement;
|
||||
feature: Feature;
|
||||
};
|
||||
|
||||
const guidePopoverSelector = createSelector(
|
||||
systemSelector,
|
||||
(system: SystemState) => system.shouldDisplayGuides
|
||||
);
|
||||
|
||||
const GuidePopover = ({ children, feature }: GuideProps) => {
|
||||
const shouldDisplayGuides = useAppSelector(guidePopoverSelector);
|
||||
const { text } = useFeatureHelpInfo(feature);
|
||||
|
||||
if (!shouldDisplayGuides) return null;
|
||||
|
||||
return (
|
||||
<Popover trigger="hover" isLazy>
|
||||
<PopoverTrigger>
|
||||
<Box>{children}</Box>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
maxWidth="400px"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
cursor="initial"
|
||||
>
|
||||
<PopoverArrow />
|
||||
<PopoverBody>{text}</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GuidePopover);
|
@ -169,7 +169,9 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
...imageSx,
|
||||
}}
|
||||
/>
|
||||
{withMetadataOverlay && <ImageMetadataOverlay image={imageDTO} />}
|
||||
{withMetadataOverlay && (
|
||||
<ImageMetadataOverlay imageDTO={imageDTO} />
|
||||
)}
|
||||
<SelectionOverlay
|
||||
isSelected={isSelected}
|
||||
isHovered={withHoverOverlay ? isHovered : false}
|
||||
|
@ -1,21 +1,11 @@
|
||||
import { Badge, Flex } from '@chakra-ui/react';
|
||||
import { isString } from 'lodash-es';
|
||||
import { useMemo } from 'react';
|
||||
import { ImageDTO } from 'services/api/types';
|
||||
|
||||
type ImageMetadataOverlayProps = {
|
||||
image: ImageDTO;
|
||||
imageDTO: ImageDTO;
|
||||
};
|
||||
|
||||
const ImageMetadataOverlay = ({ image }: ImageMetadataOverlayProps) => {
|
||||
const model = useMemo(() => {
|
||||
if (!isString(image.metadata?.model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return image.metadata?.model;
|
||||
}, [image.metadata]);
|
||||
|
||||
const ImageMetadataOverlay = ({ imageDTO }: ImageMetadataOverlayProps) => {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
@ -30,13 +20,8 @@ const ImageMetadataOverlay = ({ image }: ImageMetadataOverlayProps) => {
|
||||
}}
|
||||
>
|
||||
<Badge variant="solid" colorScheme="base">
|
||||
{image.width} × {image.height}
|
||||
{imageDTO.width} × {imageDTO.height}
|
||||
</Badge>
|
||||
{model && (
|
||||
<Badge variant="solid" colorScheme="base">
|
||||
{model}
|
||||
</Badge>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
@ -2,17 +2,16 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { validateSeedWeights } from 'common/util/seedWeightPairs';
|
||||
// import { validateSeedWeights } from 'common/util/seedWeightPairs';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { modelsApi } from '../../services/api/endpoints/models';
|
||||
import { forEach } from 'lodash-es';
|
||||
import { modelsApi } from '../../services/api/endpoints/models';
|
||||
|
||||
const readinessSelector = createSelector(
|
||||
[stateSelector, activeTabNameSelector],
|
||||
(state, activeTabName) => {
|
||||
const { generation, system } = state;
|
||||
const { shouldGenerateVariations, seedWeights, initialImage, seed } =
|
||||
generation;
|
||||
const { initialImage } = generation;
|
||||
|
||||
const { isProcessing, isConnected } = system;
|
||||
|
||||
@ -44,19 +43,19 @@ const readinessSelector = createSelector(
|
||||
reasonsWhyNotReady.push('System Disconnected');
|
||||
}
|
||||
|
||||
// Cannot generate variations without valid seed weights
|
||||
if (
|
||||
shouldGenerateVariations &&
|
||||
(!(validateSeedWeights(seedWeights) || seedWeights === '') || seed === -1)
|
||||
) {
|
||||
isReady = false;
|
||||
reasonsWhyNotReady.push('Seed-Weights badly formatted.');
|
||||
}
|
||||
// // Cannot generate variations without valid seed weights
|
||||
// if (
|
||||
// shouldGenerateVariations &&
|
||||
// (!(validateSeedWeights(seedWeights) || seedWeights === '') || seed === -1)
|
||||
// ) {
|
||||
// isReady = false;
|
||||
// reasonsWhyNotReady.push('Seed-Weights badly formatted.');
|
||||
// }
|
||||
|
||||
forEach(state.controlNet.controlNets, (controlNet, id) => {
|
||||
if (!controlNet.model) {
|
||||
isReady = false;
|
||||
reasonsWhyNotReady.push('ControlNet ${id} has no model selected.');
|
||||
reasonsWhyNotReady.push(`ControlNet ${id} has no model selected.`);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import promptToString from './promptToString';
|
||||
|
||||
export function getPromptAndNegative(inputPrompt: InvokeAI.Prompt) {
|
||||
let prompt: string =
|
||||
typeof inputPrompt === 'string' ? inputPrompt : promptToString(inputPrompt);
|
||||
|
||||
let negativePrompt = '';
|
||||
|
||||
// Matches all negative prompts, 1st capturing group is the prompt itself
|
||||
const negativePromptRegExp = new RegExp(/\[([^\][]*)]/, 'gi');
|
||||
|
||||
// Grab the actual prompt matches (capturing group 1 is 1st index of match)
|
||||
const negativePromptMatches = [...prompt.matchAll(negativePromptRegExp)].map(
|
||||
(match) => match[1]
|
||||
);
|
||||
|
||||
if (negativePromptMatches.length) {
|
||||
// Build the negative prompt itself
|
||||
negativePrompt = negativePromptMatches.join(' ');
|
||||
|
||||
// Replace each match, including its surrounding brackets
|
||||
// Remove each pair of empty brackets
|
||||
// Trim whitespace
|
||||
negativePromptMatches.forEach((match) => {
|
||||
prompt = prompt.replace(`[${match}]`, '').replaceAll('[]', '').trim();
|
||||
});
|
||||
}
|
||||
|
||||
return [prompt, negativePrompt];
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
|
||||
const promptToString = (prompt: InvokeAI.Prompt): string => {
|
||||
if (typeof prompt === 'string') {
|
||||
return prompt;
|
||||
}
|
||||
|
||||
if (prompt.length === 1) {
|
||||
return prompt[0].prompt;
|
||||
}
|
||||
|
||||
return prompt
|
||||
.map(
|
||||
(promptItem: InvokeAI.PromptItem): string =>
|
||||
`${promptItem.prompt}:${promptItem.weight}`
|
||||
)
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
export default promptToString;
|
@ -1,68 +1,71 @@
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
// TODO: Restore variations
|
||||
// Support code from v2.3 in here.
|
||||
|
||||
export const stringToSeedWeights = (
|
||||
string: string
|
||||
): InvokeAI.SeedWeights | boolean => {
|
||||
const stringPairs = string.split(',');
|
||||
const arrPairs = stringPairs.map((p) => p.split(':'));
|
||||
const pairs = arrPairs.map((p: Array<string>): InvokeAI.SeedWeightPair => {
|
||||
return { seed: Number(p[0]), weight: Number(p[1]) };
|
||||
});
|
||||
// export const stringToSeedWeights = (
|
||||
// string: string
|
||||
// ): InvokeAI.SeedWeights | boolean => {
|
||||
// const stringPairs = string.split(',');
|
||||
// const arrPairs = stringPairs.map((p) => p.split(':'));
|
||||
// const pairs = arrPairs.map((p: Array<string>): InvokeAI.SeedWeightPair => {
|
||||
// return { seed: Number(p[0]), weight: Number(p[1]) };
|
||||
// });
|
||||
|
||||
if (!validateSeedWeights(pairs)) {
|
||||
return false;
|
||||
}
|
||||
// if (!validateSeedWeights(pairs)) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return pairs;
|
||||
};
|
||||
// return pairs;
|
||||
// };
|
||||
|
||||
export const validateSeedWeights = (
|
||||
seedWeights: InvokeAI.SeedWeights | string
|
||||
): boolean => {
|
||||
return typeof seedWeights === 'string'
|
||||
? Boolean(stringToSeedWeights(seedWeights))
|
||||
: Boolean(
|
||||
seedWeights.length &&
|
||||
!seedWeights.some((pair: InvokeAI.SeedWeightPair) => {
|
||||
const { seed, weight } = pair;
|
||||
const isSeedValid = !isNaN(parseInt(seed.toString(), 10));
|
||||
const isWeightValid =
|
||||
!isNaN(parseInt(weight.toString(), 10)) &&
|
||||
weight >= 0 &&
|
||||
weight <= 1;
|
||||
return !(isSeedValid && isWeightValid);
|
||||
})
|
||||
);
|
||||
};
|
||||
// export const validateSeedWeights = (
|
||||
// seedWeights: InvokeAI.SeedWeights | string
|
||||
// ): boolean => {
|
||||
// return typeof seedWeights === 'string'
|
||||
// ? Boolean(stringToSeedWeights(seedWeights))
|
||||
// : Boolean(
|
||||
// seedWeights.length &&
|
||||
// !seedWeights.some((pair: InvokeAI.SeedWeightPair) => {
|
||||
// const { seed, weight } = pair;
|
||||
// const isSeedValid = !isNaN(parseInt(seed.toString(), 10));
|
||||
// const isWeightValid =
|
||||
// !isNaN(parseInt(weight.toString(), 10)) &&
|
||||
// weight >= 0 &&
|
||||
// weight <= 1;
|
||||
// return !(isSeedValid && isWeightValid);
|
||||
// })
|
||||
// );
|
||||
// };
|
||||
|
||||
export const seedWeightsToString = (
|
||||
seedWeights: InvokeAI.SeedWeights
|
||||
): string => {
|
||||
return seedWeights.reduce((acc, pair, i, arr) => {
|
||||
const { seed, weight } = pair;
|
||||
acc += `${seed}:${weight}`;
|
||||
if (i !== arr.length - 1) {
|
||||
acc += ',';
|
||||
}
|
||||
return acc;
|
||||
}, '');
|
||||
};
|
||||
// export const seedWeightsToString = (
|
||||
// seedWeights: InvokeAI.SeedWeights
|
||||
// ): string => {
|
||||
// return seedWeights.reduce((acc, pair, i, arr) => {
|
||||
// const { seed, weight } = pair;
|
||||
// acc += `${seed}:${weight}`;
|
||||
// if (i !== arr.length - 1) {
|
||||
// acc += ',';
|
||||
// }
|
||||
// return acc;
|
||||
// }, '');
|
||||
// };
|
||||
|
||||
export const seedWeightsToArray = (
|
||||
seedWeights: InvokeAI.SeedWeights
|
||||
): Array<Array<number>> => {
|
||||
return seedWeights.map((pair: InvokeAI.SeedWeightPair) => [
|
||||
pair.seed,
|
||||
pair.weight,
|
||||
]);
|
||||
};
|
||||
// export const seedWeightsToArray = (
|
||||
// seedWeights: InvokeAI.SeedWeights
|
||||
// ): Array<Array<number>> => {
|
||||
// return seedWeights.map((pair: InvokeAI.SeedWeightPair) => [
|
||||
// pair.seed,
|
||||
// pair.weight,
|
||||
// ]);
|
||||
// };
|
||||
|
||||
export const stringToSeedWeightsArray = (
|
||||
string: string
|
||||
): Array<Array<number>> => {
|
||||
const stringPairs = string.split(',');
|
||||
const arrPairs = stringPairs.map((p) => p.split(':'));
|
||||
return arrPairs.map(
|
||||
(p: Array<string>): Array<number> => [parseInt(p[0], 10), parseFloat(p[1])]
|
||||
);
|
||||
};
|
||||
// export const stringToSeedWeightsArray = (
|
||||
// string: string
|
||||
// ): Array<Array<number>> => {
|
||||
// const stringPairs = string.split(',');
|
||||
// const arrPairs = stringPairs.map((p) => p.split(':'));
|
||||
// return arrPairs.map(
|
||||
// (p: Array<string>): Array<number> => [parseInt(p[0], 10), parseFloat(p[1])]
|
||||
// );
|
||||
// };
|
||||
|
||||
export default {};
|
||||
|
@ -1,7 +1,5 @@
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import { IRect, Vector2d } from 'konva/lib/types';
|
||||
import { RgbaColor } from 'react-colorful';
|
||||
import { ImageDTO } from 'services/api/types';
|
||||
|
||||
export const LAYER_NAMES_DICT = [
|
||||
{ label: 'Base', value: 'base' },
|
||||
@ -133,7 +131,6 @@ export interface CanvasState {
|
||||
cursorPosition: Vector2d | null;
|
||||
doesCanvasNeedScaling: boolean;
|
||||
futureLayerStates: CanvasLayerState[];
|
||||
intermediateImage?: InvokeAI.Image;
|
||||
isCanvasInitialized: boolean;
|
||||
isDrawing: boolean;
|
||||
isMaskEnabled: boolean;
|
||||
|
@ -1,73 +0,0 @@
|
||||
import { Flex, Image, Spinner } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import { memo } from 'react';
|
||||
import { gallerySelector } from '../store/gallerySelectors';
|
||||
|
||||
const selector = createSelector(
|
||||
[systemSelector, gallerySelector],
|
||||
(system, gallery) => {
|
||||
const { shouldUseSingleGalleryColumn, galleryImageObjectFit } = gallery;
|
||||
const { progressImage, shouldAntialiasProgressImage } = system;
|
||||
|
||||
return {
|
||||
progressImage,
|
||||
shouldUseSingleGalleryColumn,
|
||||
galleryImageObjectFit,
|
||||
shouldAntialiasProgressImage,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
const GalleryProgressImage = () => {
|
||||
const {
|
||||
progressImage,
|
||||
shouldUseSingleGalleryColumn,
|
||||
galleryImageObjectFit,
|
||||
shouldAntialiasProgressImage,
|
||||
} = useAppSelector(selector);
|
||||
|
||||
if (!progressImage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
aspectRatio: '1/1',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
draggable={false}
|
||||
src={progressImage.dataURL}
|
||||
width={progressImage.width}
|
||||
height={progressImage.height}
|
||||
sx={{
|
||||
objectFit: shouldUseSingleGalleryColumn
|
||||
? 'contain'
|
||||
: galleryImageObjectFit,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
borderRadius: 'base',
|
||||
imageRendering: shouldAntialiasProgressImage ? 'auto' : 'pixelated',
|
||||
}}
|
||||
/>
|
||||
<Spinner
|
||||
sx={{ position: 'absolute', top: 1, right: 1, opacity: 0.7 }}
|
||||
speed="1.2s"
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GalleryProgressImage);
|
@ -159,6 +159,8 @@ const GalleryImageGrid = () => {
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(GalleryImageGrid);
|
||||
|
@ -1,23 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { selectBoardsAll } from './boardSlice';
|
||||
|
||||
export const boardSelector = (state: RootState) => state.boards.entities;
|
||||
|
||||
export const searchBoardsSelector = createSelector(
|
||||
(state: RootState) => state,
|
||||
(state) => {
|
||||
const {
|
||||
boards: { searchText },
|
||||
} = state;
|
||||
|
||||
if (!searchText) {
|
||||
// If no search text provided, return all entities
|
||||
return selectBoardsAll(state);
|
||||
}
|
||||
|
||||
return selectBoardsAll(state).filter((i) =>
|
||||
i.board_name.toLowerCase().includes(searchText.toLowerCase())
|
||||
);
|
||||
}
|
||||
);
|
@ -5,13 +5,6 @@ import { initialGalleryState } from './gallerySlice';
|
||||
*/
|
||||
export const galleryPersistDenylist: (keyof typeof initialGalleryState)[] = [
|
||||
'selection',
|
||||
'entities',
|
||||
'ids',
|
||||
'isLoading',
|
||||
'limit',
|
||||
'offset',
|
||||
'selectedBoardId',
|
||||
'galleryView',
|
||||
'total',
|
||||
'isInitialized',
|
||||
];
|
||||
|
@ -45,7 +45,7 @@ export const gallerySlice = createSlice({
|
||||
initialState: initialGalleryState,
|
||||
reducers: {
|
||||
imageRangeEndSelected: (state, action: PayloadAction<string>) => {
|
||||
// MULTI SELECT LOGIC
|
||||
// TODO: multiselect
|
||||
// const rangeEndImageName = action.payload;
|
||||
// const lastSelectedImage = state.selection[state.selection.length - 1];
|
||||
// const filteredImages = selectFilteredImagesLocal(state);
|
||||
@ -66,7 +66,7 @@ export const gallerySlice = createSlice({
|
||||
// }
|
||||
},
|
||||
imageSelectionToggled: (state, action: PayloadAction<string>) => {
|
||||
// MULTI SELECT LOGIC
|
||||
// TODO: multiselect
|
||||
// if (
|
||||
// state.selection.includes(action.payload) &&
|
||||
// state.selection.length > 1
|
||||
@ -157,7 +157,6 @@ export const gallerySlice = createSlice({
|
||||
});
|
||||
|
||||
export const {
|
||||
imagesRemoved,
|
||||
imageRangeEndSelected,
|
||||
imageSelectionToggled,
|
||||
imageSelected,
|
||||
|
@ -18,7 +18,13 @@ const selector = createSelector(
|
||||
const ParamLoraList = () => {
|
||||
const { loras } = useAppSelector(selector);
|
||||
|
||||
return map(loras, (lora) => <ParamLora key={lora.model_name} lora={lora} />);
|
||||
return (
|
||||
<>
|
||||
{map(loras, (lora) => (
|
||||
<ParamLora key={lora.model_name} lora={lora} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParamLoraList;
|
||||
|
@ -59,13 +59,25 @@ const ViewportControls = () => {
|
||||
return (
|
||||
<ButtonGroup isAttached orientation="vertical">
|
||||
<Tooltip label={t('nodes.zoomInNodes')}>
|
||||
<IAIIconButton onClick={handleClickedZoomIn} icon={<FaPlus />} />
|
||||
<IAIIconButton
|
||||
aria-label="Zoom in "
|
||||
onClick={handleClickedZoomIn}
|
||||
icon={<FaPlus />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label={t('nodes.zoomOutNodes')}>
|
||||
<IAIIconButton onClick={handleClickedZoomOut} icon={<FaMinus />} />
|
||||
<IAIIconButton
|
||||
aria-label="Zoom out"
|
||||
onClick={handleClickedZoomOut}
|
||||
icon={<FaMinus />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label={t('nodes.fitViewportNodes')}>
|
||||
<IAIIconButton onClick={handleClickedFitView} icon={<FaExpand />} />
|
||||
<IAIIconButton
|
||||
aria-label="Fit viewport"
|
||||
onClick={handleClickedFitView}
|
||||
icon={<FaExpand />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
label={
|
||||
@ -75,6 +87,7 @@ const ViewportControls = () => {
|
||||
}
|
||||
>
|
||||
<IAIIconButton
|
||||
aria-label="Toggle nodes graph overlay"
|
||||
isChecked={shouldShowGraphOverlay}
|
||||
onClick={handleClickedToggleGraphOverlay}
|
||||
icon={<FaCode />}
|
||||
@ -88,6 +101,7 @@ const ViewportControls = () => {
|
||||
}
|
||||
>
|
||||
<IAIIconButton
|
||||
aria-label="Toggle field type legend"
|
||||
isChecked={shouldShowFieldTypeLegend}
|
||||
onClick={handleClickedToggleFieldTypeLegend}
|
||||
icon={<FaInfo />}
|
||||
@ -101,6 +115,7 @@ const ViewportControls = () => {
|
||||
}
|
||||
>
|
||||
<IAIIconButton
|
||||
aria-label="Toggle minimap"
|
||||
isChecked={shouldShowMinimapPanel}
|
||||
onClick={handleClickedToggleMiniMapPanel}
|
||||
icon={<FaMapMarkerAlt />}
|
||||
|
@ -22,13 +22,16 @@ export default function ParamAdvancedCollapse() {
|
||||
const shouldShowAdvancedOptions = useAppSelector(
|
||||
(state: RootState) => state.ui.shouldShowAdvancedOptions
|
||||
);
|
||||
|
||||
if (!shouldShowAdvancedOptions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
shouldShowAdvancedOptions && (
|
||||
<IAICollapse label={'Advanced'} activeLabel={activeLabel}>
|
||||
<Flex sx={{ flexDir: 'column', gap: 2 }}>
|
||||
<ParamClipSkip />
|
||||
</Flex>
|
||||
</IAICollapse>
|
||||
)
|
||||
<IAICollapse label={'Advanced'} activeLabel={activeLabel}>
|
||||
<Flex sx={{ flexDir: 'column', gap: 2 }}>
|
||||
<ParamClipSkip />
|
||||
</Flex>
|
||||
</IAICollapse>
|
||||
);
|
||||
}
|
||||
|
@ -1,33 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { setSeamless } from 'features/parameters/store/generationSlice';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
/**
|
||||
* Seamless tiling toggle
|
||||
*/
|
||||
const ParamSeamlessToggle = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const seamless = useAppSelector(
|
||||
(state: RootState) => state.generation.seamless
|
||||
);
|
||||
|
||||
const handleChangeSeamless = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setSeamless(e.target.checked));
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAISwitch
|
||||
label={t('parameters.seamlessTiling')}
|
||||
fontSize="md"
|
||||
isChecked={seamless}
|
||||
onChange={handleChangeSeamless}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParamSeamlessToggle;
|
@ -1,48 +0,0 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import ParamSeed from './ParamSeed';
|
||||
import { memo, useCallback } from 'react';
|
||||
import ParamSeedShuffle from './ParamSeedShuffle';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { setShouldRandomizeSeed } from 'features/parameters/store/generationSlice';
|
||||
import IAICollapse from 'common/components/IAICollapse';
|
||||
|
||||
const selector = createSelector(
|
||||
generationSelector,
|
||||
(generation) => {
|
||||
const { shouldRandomizeSeed } = generation;
|
||||
|
||||
return { shouldRandomizeSeed };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
const ParamSeedSettings = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { shouldRandomizeSeed } = useAppSelector(selector);
|
||||
|
||||
const handleToggle = useCallback(
|
||||
() => dispatch(setShouldRandomizeSeed(!shouldRandomizeSeed)),
|
||||
[dispatch, shouldRandomizeSeed]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAICollapse
|
||||
label={t('parameters.seed')}
|
||||
isOpen={!shouldRandomizeSeed}
|
||||
onToggle={handleToggle}
|
||||
withSwitch
|
||||
>
|
||||
<Flex sx={{ gap: 4 }}>
|
||||
<ParamSeed />
|
||||
<ParamSeedShuffle />
|
||||
</Flex>
|
||||
</IAICollapse>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ParamSeedSettings);
|
@ -1,47 +1,51 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAICollapse from 'common/components/IAICollapse';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ParamVariationAmount from './ParamVariationAmount';
|
||||
import { ParamVariationToggle } from './ParamVariationToggle';
|
||||
import ParamVariationWeights from './ParamVariationWeights';
|
||||
// TODO: variations
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
(state) => {
|
||||
const activeLabel = state.generation.shouldGenerateVariations
|
||||
? 'Enabled'
|
||||
: undefined;
|
||||
// import { Flex } from '@chakra-ui/react';
|
||||
// import { createSelector } from '@reduxjs/toolkit';
|
||||
// import { stateSelector } from 'app/store/store';
|
||||
// import { useAppSelector } from 'app/store/storeHooks';
|
||||
// import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
// import IAICollapse from 'common/components/IAICollapse';
|
||||
// import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
// import { memo } from 'react';
|
||||
// import { useTranslation } from 'react-i18next';
|
||||
// import ParamVariationAmount from './ParamVariationAmount';
|
||||
// import { ParamVariationToggle } from './ParamVariationToggle';
|
||||
// import ParamVariationWeights from './ParamVariationWeights';
|
||||
|
||||
return { activeLabel };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
// const selector = createSelector(
|
||||
// stateSelector,
|
||||
// (state) => {
|
||||
// const activeLabel = state.generation.shouldGenerateVariations
|
||||
// ? 'Enabled'
|
||||
// : undefined;
|
||||
|
||||
const ParamVariationCollapse = () => {
|
||||
const { t } = useTranslation();
|
||||
const { activeLabel } = useAppSelector(selector);
|
||||
// return { activeLabel };
|
||||
// },
|
||||
// defaultSelectorOptions
|
||||
// );
|
||||
|
||||
const isVariationEnabled = useFeatureStatus('variation').isFeatureEnabled;
|
||||
// const ParamVariationCollapse = () => {
|
||||
// const { t } = useTranslation();
|
||||
// const { activeLabel } = useAppSelector(selector);
|
||||
|
||||
if (!isVariationEnabled) {
|
||||
return null;
|
||||
}
|
||||
// const isVariationEnabled = useFeatureStatus('variation').isFeatureEnabled;
|
||||
|
||||
return (
|
||||
<IAICollapse label={t('parameters.variations')} activeLabel={activeLabel}>
|
||||
<Flex sx={{ gap: 2, flexDirection: 'column' }}>
|
||||
<ParamVariationToggle />
|
||||
<ParamVariationAmount />
|
||||
<ParamVariationWeights />
|
||||
</Flex>
|
||||
</IAICollapse>
|
||||
);
|
||||
};
|
||||
// if (!isVariationEnabled) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
export default memo(ParamVariationCollapse);
|
||||
// return (
|
||||
// <IAICollapse label={t('parameters.variations')} activeLabel={activeLabel}>
|
||||
// <Flex sx={{ gap: 2, flexDirection: 'column' }}>
|
||||
// <ParamVariationToggle />
|
||||
// <ParamVariationAmount />
|
||||
// <ParamVariationWeights />
|
||||
// </Flex>
|
||||
// </IAICollapse>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default memo(ParamVariationCollapse);
|
||||
|
||||
export default {};
|
||||
|
@ -1,37 +1,41 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIInput from 'common/components/IAIInput';
|
||||
import { validateSeedWeights } from 'common/util/seedWeightPairs';
|
||||
import { setSeedWeights } from 'features/parameters/store/generationSlice';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
// TODO: variations
|
||||
|
||||
export default function ParamVariationWeights() {
|
||||
const seedWeights = useAppSelector(
|
||||
(state: RootState) => state.generation.seedWeights
|
||||
);
|
||||
// import { RootState } from 'app/store/store';
|
||||
// import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
// import IAIInput from 'common/components/IAIInput';
|
||||
// import { validateSeedWeights } from 'common/util/seedWeightPairs';
|
||||
// import { setSeedWeights } from 'features/parameters/store/generationSlice';
|
||||
// import { ChangeEvent } from 'react';
|
||||
// import { useTranslation } from 'react-i18next';
|
||||
|
||||
const shouldGenerateVariations = useAppSelector(
|
||||
(state: RootState) => state.generation.shouldGenerateVariations
|
||||
);
|
||||
// export default function ParamVariationWeights() {
|
||||
// const seedWeights = useAppSelector(
|
||||
// (state: RootState) => state.generation.seedWeights
|
||||
// );
|
||||
|
||||
const { t } = useTranslation();
|
||||
// const shouldGenerateVariations = useAppSelector(
|
||||
// (state: RootState) => state.generation.shouldGenerateVariations
|
||||
// );
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
// const { t } = useTranslation();
|
||||
|
||||
const handleChangeSeedWeights = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setSeedWeights(e.target.value));
|
||||
// const dispatch = useAppDispatch();
|
||||
|
||||
return (
|
||||
<IAIInput
|
||||
label={t('parameters.seedWeights')}
|
||||
value={seedWeights}
|
||||
isInvalid={
|
||||
shouldGenerateVariations &&
|
||||
!(validateSeedWeights(seedWeights) || seedWeights === '')
|
||||
}
|
||||
isDisabled={!shouldGenerateVariations}
|
||||
onChange={handleChangeSeedWeights}
|
||||
/>
|
||||
);
|
||||
}
|
||||
// const handleChangeSeedWeights = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
// dispatch(setSeedWeights(e.target.value));
|
||||
|
||||
// return (
|
||||
// <IAIInput
|
||||
// label={t('parameters.seedWeights')}
|
||||
// value={seedWeights}
|
||||
// isInvalid={
|
||||
// shouldGenerateVariations &&
|
||||
// !(validateSeedWeights(seedWeights) || seedWeights === '')
|
||||
// }
|
||||
// isDisabled={!shouldGenerateVariations}
|
||||
// onChange={handleChangeSeedWeights}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
|
||||
export default {};
|
||||
|
@ -1,47 +1,11 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { pickBy, reduce } from 'lodash-es';
|
||||
|
||||
export const systemSelector = (state: RootState) => state.system;
|
||||
|
||||
export const toastQueueSelector = (state: RootState) => state.system.toastQueue;
|
||||
|
||||
export const activeModelSelector = createSelector(
|
||||
systemSelector,
|
||||
(system) => {
|
||||
const { model_list } = system;
|
||||
const activeModel = reduce(
|
||||
model_list,
|
||||
(acc, model, key) => {
|
||||
if (model.status === 'active') {
|
||||
acc = key;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
''
|
||||
);
|
||||
return { ...model_list[activeModel], name: activeModel };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
export const diffusersModelsSelector = createSelector(
|
||||
systemSelector,
|
||||
(system) => {
|
||||
const { model_list } = system;
|
||||
|
||||
const diffusersModels = pickBy(model_list, (model, key) => {
|
||||
if (model.format === 'diffusers') {
|
||||
return { name: key, ...model };
|
||||
}
|
||||
});
|
||||
|
||||
return diffusersModels;
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
export const languageSelector = createSelector(
|
||||
systemSelector,
|
||||
(system) => system.language,
|
||||
|
@ -74,7 +74,8 @@ export interface SystemState {
|
||||
*/
|
||||
consoleLogLevel: InvokeLogLevel;
|
||||
shouldLogToConsole: boolean;
|
||||
statusTranslationKey: any;
|
||||
// TODO: probably better to not store keys here, should just be a string that maps to the translation key
|
||||
statusTranslationKey: string;
|
||||
/**
|
||||
* When a session is canceled, its ID is stored here until a new session is created.
|
||||
*/
|
||||
@ -125,7 +126,7 @@ export const systemSlice = createSlice({
|
||||
setIsProcessing: (state, action: PayloadAction<boolean>) => {
|
||||
state.isProcessing = action.payload;
|
||||
},
|
||||
setCurrentStatus: (state, action: any) => {
|
||||
setCurrentStatus: (state, action: PayloadAction<string>) => {
|
||||
state.statusTranslationKey = action.payload;
|
||||
},
|
||||
setShouldConfirmOnDelete: (state, action: PayloadAction<boolean>) => {
|
||||
@ -362,7 +363,7 @@ export const systemSlice = createSlice({
|
||||
* Session Invoked - REJECTED
|
||||
* Session Created - REJECTED
|
||||
*/
|
||||
builder.addMatcher(isAnySessionRejected, (state, action) => {
|
||||
builder.addMatcher(isAnySessionRejected, (state) => {
|
||||
state.isProcessing = false;
|
||||
state.isCancelable = false;
|
||||
state.isCancelScheduled = false;
|
||||
|
@ -1,43 +0,0 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||
import InitialImageDisplay from 'features/parameters/components/Parameters/ImageToImage/InitialImageDisplay';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import {
|
||||
ImperativePanelGroupHandle,
|
||||
Panel,
|
||||
PanelGroup,
|
||||
} from 'react-resizable-panels';
|
||||
import ResizeHandle from '../ResizeHandle';
|
||||
import TextToImageTabMain from '../TextToImage/TextToImageTabMain';
|
||||
import BatchManager from 'features/batch/components/BatchManager';
|
||||
|
||||
const ImageToImageTab = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||
|
||||
const handleDoubleClickHandle = useCallback(() => {
|
||||
if (!panelGroupRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
panelGroupRef.current.setLayout([50, 50]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
layerStyle={'first'}
|
||||
sx={{
|
||||
gap: 4,
|
||||
p: 4,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
>
|
||||
<BatchManager />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ImageToImageTab);
|
@ -7,9 +7,8 @@ import ParamPositiveConditioning from 'features/parameters/components/Parameters
|
||||
import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/ParamNoiseCollapse';
|
||||
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
||||
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
||||
import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
||||
// import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
||||
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
|
||||
import { memo } from 'react';
|
||||
import ImageToImageTabCoreParameters from './ImageToImageTabCoreParameters';
|
||||
|
||||
const ImageToImageTabParameters = () => {
|
||||
@ -22,7 +21,7 @@ const ImageToImageTabParameters = () => {
|
||||
<ParamControlNetCollapse />
|
||||
<ParamLoraCollapse />
|
||||
<ParamDynamicPromptsCollapse />
|
||||
<ParamVariationCollapse />
|
||||
{/* <ParamVariationCollapse /> */}
|
||||
<ParamNoiseCollapse />
|
||||
<ParamSymmetryCollapse />
|
||||
<ParamSeamlessCollapse />
|
||||
@ -31,4 +30,4 @@ const ImageToImageTabParameters = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ImageToImageTabParameters);
|
||||
export default ImageToImageTabParameters;
|
||||
|
@ -168,7 +168,9 @@ export default function FoundModelsList() {
|
||||
};
|
||||
|
||||
const renderFoundModels = () => {
|
||||
if (!searchFolder) return;
|
||||
if (!searchFolder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!foundModels || foundModels.length === 0) {
|
||||
return (
|
||||
@ -242,7 +244,7 @@ const foundModelsFilter = (
|
||||
const filteredModels: SearchFolderResponse = [];
|
||||
forEach(data, (model) => {
|
||||
if (!model) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (model.includes(nameFilter)) {
|
||||
|
@ -32,66 +32,68 @@ export default function ScanAdvancedAddModels() {
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
if (!advancedAddScanModel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
advancedAddScanModel && (
|
||||
<Box
|
||||
as={motion.div}
|
||||
initial={{ x: -100, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1, transition: { duration: 0.2 } }}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minWidth: '40%',
|
||||
maxHeight: window.innerHeight - 300,
|
||||
overflow: 'scroll',
|
||||
p: 4,
|
||||
gap: 4,
|
||||
borderRadius: 4,
|
||||
bg: 'base.200',
|
||||
_dark: {
|
||||
bg: 'base.800',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<Text size="xl" fontWeight={600}>
|
||||
{isCheckpoint || advancedAddMode === 'checkpoint'
|
||||
? 'Add Checkpoint Model'
|
||||
: 'Add Diffusers Model'}
|
||||
</Text>
|
||||
<IAIIconButton
|
||||
icon={<FaTimes />}
|
||||
aria-label="Close Advanced"
|
||||
onClick={() => dispatch(setAdvancedAddScanModel(null))}
|
||||
size="sm"
|
||||
/>
|
||||
</Flex>
|
||||
<IAIMantineSelect
|
||||
label="Model Type"
|
||||
value={advancedAddMode}
|
||||
data={advancedAddModeData}
|
||||
onChange={(v) => {
|
||||
if (!v) return;
|
||||
setAdvancedAddMode(v as ManualAddMode);
|
||||
if (v === 'checkpoint') {
|
||||
setIsCheckpoint(true);
|
||||
} else {
|
||||
setIsCheckpoint(false);
|
||||
}
|
||||
}}
|
||||
<Box
|
||||
as={motion.div}
|
||||
initial={{ x: -100, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1, transition: { duration: 0.2 } }}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minWidth: '40%',
|
||||
maxHeight: window.innerHeight - 300,
|
||||
overflow: 'scroll',
|
||||
p: 4,
|
||||
gap: 4,
|
||||
borderRadius: 4,
|
||||
bg: 'base.200',
|
||||
_dark: {
|
||||
bg: 'base.800',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<Text size="xl" fontWeight={600}>
|
||||
{isCheckpoint || advancedAddMode === 'checkpoint'
|
||||
? 'Add Checkpoint Model'
|
||||
: 'Add Diffusers Model'}
|
||||
</Text>
|
||||
<IAIIconButton
|
||||
icon={<FaTimes />}
|
||||
aria-label="Close Advanced"
|
||||
onClick={() => dispatch(setAdvancedAddScanModel(null))}
|
||||
size="sm"
|
||||
/>
|
||||
{isCheckpoint ? (
|
||||
<AdvancedAddCheckpoint
|
||||
key={advancedAddScanModel}
|
||||
model_path={advancedAddScanModel}
|
||||
/>
|
||||
) : (
|
||||
<AdvancedAddDiffusers
|
||||
key={advancedAddScanModel}
|
||||
model_path={advancedAddScanModel}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
</Flex>
|
||||
<IAIMantineSelect
|
||||
label="Model Type"
|
||||
value={advancedAddMode}
|
||||
data={advancedAddModeData}
|
||||
onChange={(v) => {
|
||||
if (!v) return;
|
||||
setAdvancedAddMode(v as ManualAddMode);
|
||||
if (v === 'checkpoint') {
|
||||
setIsCheckpoint(true);
|
||||
} else {
|
||||
setIsCheckpoint(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{isCheckpoint ? (
|
||||
<AdvancedAddCheckpoint
|
||||
key={advancedAddScanModel}
|
||||
model_path={advancedAddScanModel}
|
||||
/>
|
||||
) : (
|
||||
<AdvancedAddDiffusers
|
||||
key={advancedAddScanModel}
|
||||
model_path={advancedAddScanModel}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -1,430 +0,0 @@
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Flex,
|
||||
FormControl,
|
||||
HStack,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Spacer,
|
||||
Text,
|
||||
} from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { FaSearch, FaTrash } from 'react-icons/fa';
|
||||
|
||||
// import { addNewModel, searchForModels } from 'app/socketio/actions';
|
||||
import {
|
||||
setFoundModels,
|
||||
setSearchFolder,
|
||||
} from 'features/system/store/systemSlice';
|
||||
import { setShouldShowExistingModelsInSearch } from 'features/ui/store/uiSlice';
|
||||
|
||||
import type { FoundModel } from 'app/types/invokeai';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import IAIInput from 'common/components/IAIInput';
|
||||
import { Field, Formik } from 'formik';
|
||||
import { forEach, remove } from 'lodash-es';
|
||||
import type { ChangeEvent, ReactNode } from 'react';
|
||||
import IAIForm from 'common/components/IAIForm';
|
||||
|
||||
const existingModelsSelector = createSelector([systemSelector], (system) => {
|
||||
const { model_list } = system;
|
||||
|
||||
const existingModels: string[] = [];
|
||||
|
||||
forEach(model_list, (value) => {
|
||||
existingModels.push(value.weights);
|
||||
});
|
||||
|
||||
return existingModels;
|
||||
});
|
||||
|
||||
interface SearchModelEntry {
|
||||
model: FoundModel;
|
||||
modelsToAdd: string[];
|
||||
setModelsToAdd: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
}
|
||||
|
||||
function SearchModelEntry({
|
||||
model,
|
||||
modelsToAdd,
|
||||
setModelsToAdd,
|
||||
}: SearchModelEntry) {
|
||||
const { t } = useTranslation();
|
||||
const existingModels = useAppSelector(existingModelsSelector);
|
||||
|
||||
const foundModelsChangeHandler = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!modelsToAdd.includes(e.target.value)) {
|
||||
setModelsToAdd([...modelsToAdd, e.target.value]);
|
||||
} else {
|
||||
setModelsToAdd(remove(modelsToAdd, (v) => v !== e.target.value));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
gap={2}
|
||||
backgroundColor={
|
||||
modelsToAdd.includes(model.name) ? 'accent.650' : 'base.800'
|
||||
}
|
||||
paddingX={4}
|
||||
paddingY={2}
|
||||
borderRadius={4}
|
||||
>
|
||||
<Flex gap={4} alignItems="center" justifyContent="space-between">
|
||||
<IAISimpleCheckbox
|
||||
value={model.name}
|
||||
label={<Text fontWeight={500}>{model.name}</Text>}
|
||||
isChecked={modelsToAdd.includes(model.name)}
|
||||
isDisabled={existingModels.includes(model.location)}
|
||||
onChange={foundModelsChangeHandler}
|
||||
></IAISimpleCheckbox>
|
||||
{existingModels.includes(model.location) && (
|
||||
<Badge colorScheme="accent">{t('modelManager.modelExists')}</Badge>
|
||||
)}
|
||||
</Flex>
|
||||
<Text fontStyle="italic" variant="subtext">
|
||||
{model.location}
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SearchModels() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const searchFolder = useAppSelector(
|
||||
(state: RootState) => state.system.searchFolder
|
||||
);
|
||||
|
||||
const foundModels = useAppSelector(
|
||||
(state: RootState) => state.system.foundModels
|
||||
);
|
||||
|
||||
const existingModels = useAppSelector(existingModelsSelector);
|
||||
|
||||
const shouldShowExistingModelsInSearch = useAppSelector(
|
||||
(state: RootState) => state.ui.shouldShowExistingModelsInSearch
|
||||
);
|
||||
|
||||
const isProcessing = useAppSelector(
|
||||
(state: RootState) => state.system.isProcessing
|
||||
);
|
||||
|
||||
const [modelsToAdd, setModelsToAdd] = React.useState<string[]>([]);
|
||||
const [modelType, setModelType] = React.useState<string>('v1');
|
||||
const [pathToConfig, setPathToConfig] = React.useState<string>('');
|
||||
|
||||
const resetSearchModelHandler = () => {
|
||||
dispatch(setSearchFolder(null));
|
||||
dispatch(setFoundModels(null));
|
||||
setModelsToAdd([]);
|
||||
};
|
||||
|
||||
const findModelsHandler = (values: { checkpointFolder: string }) => {
|
||||
dispatch(searchForModels(values.checkpointFolder));
|
||||
};
|
||||
|
||||
const addAllToSelected = () => {
|
||||
setModelsToAdd([]);
|
||||
if (foundModels) {
|
||||
foundModels.forEach((model) => {
|
||||
if (!existingModels.includes(model.location)) {
|
||||
setModelsToAdd((currentModels) => {
|
||||
return [...currentModels, model.name];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const removeAllFromSelected = () => {
|
||||
setModelsToAdd([]);
|
||||
};
|
||||
|
||||
const addSelectedModels = () => {
|
||||
const modelsToBeAdded = foundModels?.filter((foundModel) =>
|
||||
modelsToAdd.includes(foundModel.name)
|
||||
);
|
||||
|
||||
const configFiles = {
|
||||
v1: 'configs/stable-diffusion/v1-inference.yaml',
|
||||
v2_base: 'configs/stable-diffusion/v2-inference-v.yaml',
|
||||
v2_768: 'configs/stable-diffusion/v2-inference-v.yaml',
|
||||
inpainting: 'configs/stable-diffusion/v1-inpainting-inference.yaml',
|
||||
custom: pathToConfig,
|
||||
};
|
||||
|
||||
modelsToBeAdded?.forEach((model) => {
|
||||
const modelFormat = {
|
||||
name: model.name,
|
||||
description: '',
|
||||
config: configFiles[modelType as keyof typeof configFiles],
|
||||
weights: model.location,
|
||||
vae: '',
|
||||
width: 512,
|
||||
height: 512,
|
||||
default: false,
|
||||
format: 'ckpt',
|
||||
};
|
||||
dispatch(addNewModel(modelFormat));
|
||||
});
|
||||
setModelsToAdd([]);
|
||||
};
|
||||
|
||||
const renderFoundModels = () => {
|
||||
const newFoundModels: ReactNode[] = [];
|
||||
const existingFoundModels: ReactNode[] = [];
|
||||
|
||||
if (foundModels) {
|
||||
foundModels.forEach((model, index) => {
|
||||
if (existingModels.includes(model.location)) {
|
||||
existingFoundModels.push(
|
||||
<SearchModelEntry
|
||||
key={index}
|
||||
model={model}
|
||||
modelsToAdd={modelsToAdd}
|
||||
setModelsToAdd={setModelsToAdd}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
newFoundModels.push(
|
||||
<SearchModelEntry
|
||||
key={index}
|
||||
model={model}
|
||||
modelsToAdd={modelsToAdd}
|
||||
setModelsToAdd={setModelsToAdd}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex flexDirection="column" rowGap={4}>
|
||||
{newFoundModels}
|
||||
{shouldShowExistingModelsInSearch && existingFoundModels}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{searchFolder ? (
|
||||
<Flex
|
||||
sx={{
|
||||
padding: 4,
|
||||
gap: 2,
|
||||
position: 'relative',
|
||||
borderRadius: 'base',
|
||||
alignItems: 'center',
|
||||
w: 'full',
|
||||
bg: 'base.900',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
}}
|
||||
variant="subtext"
|
||||
>
|
||||
{t('modelManager.checkpointFolder')}
|
||||
</Text>
|
||||
<Text sx={{ fontWeight: 500 }}>{searchFolder}</Text>
|
||||
</Flex>
|
||||
<Spacer />
|
||||
<IAIIconButton
|
||||
aria-label={t('modelManager.scanAgain')}
|
||||
tooltip={t('modelManager.scanAgain')}
|
||||
icon={<FaSearch />}
|
||||
fontSize={18}
|
||||
disabled={isProcessing}
|
||||
onClick={() => dispatch(searchForModels(searchFolder))}
|
||||
/>
|
||||
<IAIIconButton
|
||||
aria-label={t('modelManager.clearCheckpointFolder')}
|
||||
tooltip={t('modelManager.clearCheckpointFolder')}
|
||||
icon={<FaTrash />}
|
||||
onClick={resetSearchModelHandler}
|
||||
/>
|
||||
</Flex>
|
||||
) : (
|
||||
<Formik
|
||||
initialValues={{ checkpointFolder: '' }}
|
||||
onSubmit={(values) => {
|
||||
findModelsHandler(values);
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit }) => (
|
||||
<IAIForm onSubmit={handleSubmit} width="100%">
|
||||
<HStack columnGap={2} alignItems="flex-end">
|
||||
<FormControl flexGrow={1}>
|
||||
<Field
|
||||
as={IAIInput}
|
||||
id="checkpointFolder"
|
||||
name="checkpointFolder"
|
||||
type="text"
|
||||
size="md"
|
||||
label={t('modelManager.checkpointFolder')}
|
||||
/>
|
||||
</FormControl>
|
||||
<IAIButton
|
||||
leftIcon={<FaSearch />}
|
||||
aria-label={t('modelManager.findModels')}
|
||||
tooltip={t('modelManager.findModels')}
|
||||
type="submit"
|
||||
disabled={isProcessing}
|
||||
px={8}
|
||||
>
|
||||
{t('modelManager.findModels')}
|
||||
</IAIButton>
|
||||
</HStack>
|
||||
</IAIForm>
|
||||
)}
|
||||
</Formik>
|
||||
)}
|
||||
{foundModels && (
|
||||
<Flex flexDirection="column" rowGap={4} width="full">
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<p>
|
||||
{t('modelManager.modelsFound')}: {foundModels.length}
|
||||
</p>
|
||||
<p>
|
||||
{t('modelManager.selected')}: {modelsToAdd.length}
|
||||
</p>
|
||||
</Flex>
|
||||
<Flex columnGap={2} justifyContent="space-between">
|
||||
<Flex columnGap={2}>
|
||||
<IAIButton
|
||||
isDisabled={modelsToAdd.length === foundModels.length}
|
||||
onClick={addAllToSelected}
|
||||
>
|
||||
{t('modelManager.selectAll')}
|
||||
</IAIButton>
|
||||
<IAIButton
|
||||
isDisabled={modelsToAdd.length === 0}
|
||||
onClick={removeAllFromSelected}
|
||||
>
|
||||
{t('modelManager.deselectAll')}
|
||||
</IAIButton>
|
||||
<IAISimpleCheckbox
|
||||
label={t('modelManager.showExisting')}
|
||||
isChecked={shouldShowExistingModelsInSearch}
|
||||
onChange={() =>
|
||||
dispatch(
|
||||
setShouldShowExistingModelsInSearch(
|
||||
!shouldShowExistingModelsInSearch
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<IAIButton
|
||||
isDisabled={modelsToAdd.length === 0}
|
||||
onClick={addSelectedModels}
|
||||
colorScheme="accent"
|
||||
>
|
||||
{t('modelManager.addSelected')}
|
||||
</IAIButton>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
padding: 4,
|
||||
rowGap: 4,
|
||||
borderRadius: 'base',
|
||||
width: 'full',
|
||||
bg: 'base.900',
|
||||
}}
|
||||
>
|
||||
<Flex gap={4}>
|
||||
<Text fontWeight={500} variant="subtext">
|
||||
{t('modelManager.pickModelType')}
|
||||
</Text>
|
||||
<RadioGroup
|
||||
value={modelType}
|
||||
onChange={(v) => setModelType(v)}
|
||||
defaultValue="v1"
|
||||
name="model_type"
|
||||
>
|
||||
<Flex gap={4}>
|
||||
<Radio value="v1">
|
||||
<Text fontSize="sm">{t('modelManager.v1')}</Text>
|
||||
</Radio>
|
||||
<Radio value="v2_base">
|
||||
<Text fontSize="sm">{t('modelManager.v2_base')}</Text>
|
||||
</Radio>
|
||||
<Radio value="v2_768">
|
||||
<Text fontSize="sm">{t('modelManager.v2_768')}</Text>
|
||||
</Radio>
|
||||
<Radio value="inpainting">
|
||||
<Text fontSize="sm">{t('modelManager.inpainting')}</Text>
|
||||
</Radio>
|
||||
<Radio value="custom">
|
||||
<Text fontSize="sm">{t('modelManager.customConfig')}</Text>
|
||||
</Radio>
|
||||
</Flex>
|
||||
</RadioGroup>
|
||||
</Flex>
|
||||
|
||||
{modelType === 'custom' && (
|
||||
<Flex flexDirection="column" rowGap={2}>
|
||||
<Text fontWeight="500" fontSize="sm" variant="subtext">
|
||||
{t('modelManager.pathToCustomConfig')}
|
||||
</Text>
|
||||
<IAIInput
|
||||
value={pathToConfig}
|
||||
onChange={(e) => {
|
||||
if (e.target.value !== '') setPathToConfig(e.target.value);
|
||||
}}
|
||||
width="full"
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
maxHeight={72}
|
||||
overflowY="scroll"
|
||||
borderRadius="sm"
|
||||
gap={2}
|
||||
>
|
||||
{foundModels.length > 0 ? (
|
||||
renderFoundModels()
|
||||
) : (
|
||||
<Text
|
||||
fontWeight="500"
|
||||
padding={2}
|
||||
borderRadius="sm"
|
||||
textAlign="center"
|
||||
variant="subtext"
|
||||
>
|
||||
{t('modelManager.noModelsFound')}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -7,9 +7,8 @@ import ParamPositiveConditioning from 'features/parameters/components/Parameters
|
||||
import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/ParamNoiseCollapse';
|
||||
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
||||
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
||||
import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
||||
// import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
||||
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
|
||||
import { memo } from 'react';
|
||||
import TextToImageTabCoreParameters from './TextToImageTabCoreParameters';
|
||||
|
||||
const TextToImageTabParameters = () => {
|
||||
@ -22,7 +21,7 @@ const TextToImageTabParameters = () => {
|
||||
<ParamControlNetCollapse />
|
||||
<ParamLoraCollapse />
|
||||
<ParamDynamicPromptsCollapse />
|
||||
<ParamVariationCollapse />
|
||||
{/* <ParamVariationCollapse /> */}
|
||||
<ParamNoiseCollapse />
|
||||
<ParamSymmetryCollapse />
|
||||
<ParamSeamlessCollapse />
|
||||
@ -31,4 +30,4 @@ const TextToImageTabParameters = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(TextToImageTabParameters);
|
||||
export default TextToImageTabParameters;
|
||||
|
@ -7,9 +7,8 @@ import ParamControlNetCollapse from 'features/parameters/components/Parameters/C
|
||||
import ParamNegativeConditioning from 'features/parameters/components/Parameters/Core/ParamNegativeConditioning';
|
||||
import ParamPositiveConditioning from 'features/parameters/components/Parameters/Core/ParamPositiveConditioning';
|
||||
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
||||
import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
||||
// import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
|
||||
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
|
||||
import { memo } from 'react';
|
||||
import UnifiedCanvasCoreParameters from './UnifiedCanvasCoreParameters';
|
||||
|
||||
const UnifiedCanvasParameters = () => {
|
||||
@ -22,7 +21,7 @@ const UnifiedCanvasParameters = () => {
|
||||
<ParamControlNetCollapse />
|
||||
<ParamLoraCollapse />
|
||||
<ParamDynamicPromptsCollapse />
|
||||
<ParamVariationCollapse />
|
||||
{/* <ParamVariationCollapse /> */}
|
||||
<ParamSymmetryCollapse />
|
||||
<ParamSeamCorrectionCollapse />
|
||||
<ParamInfillAndScalingCollapse />
|
||||
@ -31,4 +30,4 @@ const UnifiedCanvasParameters = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(UnifiedCanvasParameters);
|
||||
export default UnifiedCanvasParameters;
|
||||
|
@ -4,7 +4,7 @@ import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
||||
import { SchedulerParam } from 'features/parameters/types/parameterSchemas';
|
||||
import { setActiveTabReducer } from './extraReducers';
|
||||
import { InvokeTabName } from './tabMap';
|
||||
import { AddNewModelType, UIState } from './uiTypes';
|
||||
import { UIState } from './uiTypes';
|
||||
|
||||
export const initialUIState: UIState = {
|
||||
activeTab: 0,
|
||||
@ -14,7 +14,6 @@ export const initialUIState: UIState = {
|
||||
shouldUseCanvasBetaLayout: false,
|
||||
shouldShowExistingModelsInSearch: false,
|
||||
shouldUseSliders: false,
|
||||
addNewModelUIOption: null,
|
||||
shouldPinGallery: true,
|
||||
shouldShowGallery: true,
|
||||
shouldHidePreview: false,
|
||||
@ -57,9 +56,6 @@ export const uiSlice = createSlice({
|
||||
setShouldUseSliders: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldUseSliders = action.payload;
|
||||
},
|
||||
setAddNewModelUIOption: (state, action: PayloadAction<AddNewModelType>) => {
|
||||
state.addNewModelUIOption = action.payload;
|
||||
},
|
||||
setShouldShowGallery: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldShowGallery = action.payload;
|
||||
},
|
||||
@ -124,7 +120,6 @@ export const {
|
||||
setShouldUseCanvasBetaLayout,
|
||||
setShouldShowExistingModelsInSearch,
|
||||
setShouldUseSliders,
|
||||
setAddNewModelUIOption,
|
||||
setShouldHidePreview,
|
||||
setShouldShowGallery,
|
||||
togglePanels,
|
||||
|
@ -1,36 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ImageField } from './ImageField';
|
||||
|
||||
/**
|
||||
* Applies HED edge detection to image
|
||||
*/
|
||||
export type HedImageProcessorInvocation = {
|
||||
/**
|
||||
* The id of this node. Must be unique among all nodes.
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Whether or not this node is an intermediate node.
|
||||
*/
|
||||
is_intermediate?: boolean;
|
||||
type?: 'hed_image_processor';
|
||||
/**
|
||||
* The image to process
|
||||
*/
|
||||
image?: ImageField;
|
||||
/**
|
||||
* The pixel resolution for detection
|
||||
*/
|
||||
detect_resolution?: number;
|
||||
/**
|
||||
* The pixel resolution for the output image
|
||||
*/
|
||||
image_resolution?: number;
|
||||
/**
|
||||
* Whether to use scribble mode
|
||||
*/
|
||||
scribble?: boolean;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user