feat(ui): tidy up model manager styling

fixes #2970
This commit is contained in:
psychedelicious 2023-03-19 16:21:58 +11:00
parent 6ecf53078f
commit 93b38707b2
7 changed files with 80 additions and 90 deletions

View File

@ -0,0 +1,8 @@
import { chakra } from '@chakra-ui/react';
/**
* Chakra-enabled <form />
*/
const IAIForm = chakra.form;
export default IAIForm;

View File

@ -25,10 +25,9 @@ import { useTranslation } from 'react-i18next';
import type { InvokeModelConfigProps } from 'app/invokeai'; import type { InvokeModelConfigProps } from 'app/invokeai';
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
import IAIIconButton from 'common/components/IAIIconButton';
import { setAddNewModelUIOption } from 'features/ui/store/uiSlice'; import { setAddNewModelUIOption } from 'features/ui/store/uiSlice';
import type { FieldInputProps, FormikProps } from 'formik'; import type { FieldInputProps, FormikProps } from 'formik';
import { BiArrowBack } from 'react-icons/bi'; import IAIForm from 'common/components/IAIForm';
const MIN_MODEL_SIZE = 64; const MIN_MODEL_SIZE = 64;
const MAX_MODEL_SIZE = 2048; const MAX_MODEL_SIZE = 2048;
@ -72,19 +71,6 @@ export default function AddCheckpointModel() {
return ( return (
<VStack gap={2} alignItems="flex-start"> <VStack gap={2} alignItems="flex-start">
<IAIIconButton
aria-label={t('common.back')}
tooltip={t('common.back')}
onClick={() => dispatch(setAddNewModelUIOption(null))}
width="max-content"
position="absolute"
zIndex={1}
size="sm"
insetInlineEnd={12}
top={3}
icon={<BiArrowBack />}
/>
<SearchModels /> <SearchModels />
<IAICheckbox <IAICheckbox
label={t('modelManager.addManually')} label={t('modelManager.addManually')}
@ -98,7 +84,7 @@ export default function AddCheckpointModel() {
onSubmit={addModelFormSubmitHandler} onSubmit={addModelFormSubmitHandler}
> >
{({ handleSubmit, errors, touched }) => ( {({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}> <IAIForm onSubmit={handleSubmit} sx={{ w: 'full' }}>
<VStack rowGap={2}> <VStack rowGap={2}>
<Text fontSize={20} fontWeight="bold" alignSelf="start"> <Text fontSize={20} fontWeight="bold" alignSelf="start">
{t('modelManager.manual')} {t('modelManager.manual')}
@ -118,7 +104,7 @@ export default function AddCheckpointModel() {
name="name" name="name"
type="text" type="text"
validate={baseValidation} validate={baseValidation}
width="2xl" width="full"
/> />
{!!errors.name && touched.name ? ( {!!errors.name && touched.name ? (
<FormErrorMessage>{errors.name}</FormErrorMessage> <FormErrorMessage>{errors.name}</FormErrorMessage>
@ -144,7 +130,7 @@ export default function AddCheckpointModel() {
id="description" id="description"
name="description" name="description"
type="text" type="text"
width="2xl" width="full"
/> />
{!!errors.description && touched.description ? ( {!!errors.description && touched.description ? (
<FormErrorMessage>{errors.description}</FormErrorMessage> <FormErrorMessage>{errors.description}</FormErrorMessage>
@ -170,7 +156,7 @@ export default function AddCheckpointModel() {
id="config" id="config"
name="config" name="config"
type="text" type="text"
width="2xl" width="full"
/> />
{!!errors.config && touched.config ? ( {!!errors.config && touched.config ? (
<FormErrorMessage>{errors.config}</FormErrorMessage> <FormErrorMessage>{errors.config}</FormErrorMessage>
@ -196,7 +182,7 @@ export default function AddCheckpointModel() {
id="weights" id="weights"
name="weights" name="weights"
type="text" type="text"
width="2xl" width="full"
/> />
{!!errors.weights && touched.weights ? ( {!!errors.weights && touched.weights ? (
<FormErrorMessage>{errors.weights}</FormErrorMessage> <FormErrorMessage>{errors.weights}</FormErrorMessage>
@ -219,7 +205,7 @@ export default function AddCheckpointModel() {
id="vae" id="vae"
name="vae" name="vae"
type="text" type="text"
width="2xl" width="full"
/> />
{!!errors.vae && touched.vae ? ( {!!errors.vae && touched.vae ? (
<FormErrorMessage>{errors.vae}</FormErrorMessage> <FormErrorMessage>{errors.vae}</FormErrorMessage>
@ -231,7 +217,7 @@ export default function AddCheckpointModel() {
</VStack> </VStack>
</FormControl> </FormControl>
<HStack width="100%"> <HStack width="100%" gap={8}>
{/* Width */} {/* Width */}
<FormControl isInvalid={!!errors.width && touched.width}> <FormControl isInvalid={!!errors.width && touched.width}>
<FormLabel htmlFor="width" fontSize="sm"> <FormLabel htmlFor="width" fontSize="sm">
@ -252,7 +238,6 @@ export default function AddCheckpointModel() {
min={MIN_MODEL_SIZE} min={MIN_MODEL_SIZE}
max={MAX_MODEL_SIZE} max={MAX_MODEL_SIZE}
step={64} step={64}
width="90%"
value={form.values.width} value={form.values.width}
onChange={(value) => onChange={(value) =>
form.setFieldValue(field.name, Number(value)) form.setFieldValue(field.name, Number(value))
@ -290,7 +275,6 @@ export default function AddCheckpointModel() {
name="height" name="height"
min={MIN_MODEL_SIZE} min={MIN_MODEL_SIZE}
max={MAX_MODEL_SIZE} max={MAX_MODEL_SIZE}
width="90%"
step={64} step={64}
value={form.values.height} value={form.values.height}
onChange={(value) => onChange={(value) =>
@ -319,7 +303,7 @@ export default function AddCheckpointModel() {
{t('modelManager.addModel')} {t('modelManager.addModel')}
</IAIButton> </IAIButton>
</VStack> </VStack>
</form> </IAIForm>
)} )}
</Formik> </Formik>
)} )}

View File

@ -11,15 +11,14 @@ import { InvokeDiffusersModelConfigProps } from 'app/invokeai';
import { addNewModel } from 'app/socketio/actions'; import { addNewModel } from 'app/socketio/actions';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIButton from 'common/components/IAIButton'; import IAIButton from 'common/components/IAIButton';
import IAIIconButton from 'common/components/IAIIconButton';
import IAIInput from 'common/components/IAIInput'; import IAIInput from 'common/components/IAIInput';
import { setAddNewModelUIOption } from 'features/ui/store/uiSlice'; import { setAddNewModelUIOption } from 'features/ui/store/uiSlice';
import { Field, Formik } from 'formik'; import { Field, Formik } from 'formik';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { BiArrowBack } from 'react-icons/bi';
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import IAIForm from 'common/components/IAIForm';
function FormItemWrapper({ function FormItemWrapper({
children, children,
@ -89,24 +88,12 @@ export default function AddDiffusersModel() {
return ( return (
<Flex> <Flex>
<IAIIconButton
aria-label={t('common.back')}
tooltip={t('common.back')}
onClick={() => dispatch(setAddNewModelUIOption(null))}
width="max-content"
position="absolute"
zIndex={1}
size="sm"
insetInlineEnd={12}
top={3}
icon={<BiArrowBack />}
/>
<Formik <Formik
initialValues={addModelFormValues} initialValues={addModelFormValues}
onSubmit={addModelFormSubmitHandler} onSubmit={addModelFormSubmitHandler}
> >
{({ handleSubmit, errors, touched }) => ( {({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}> <IAIForm onSubmit={handleSubmit}>
<VStack rowGap={2}> <VStack rowGap={2}>
<FormItemWrapper> <FormItemWrapper>
{/* Name */} {/* Name */}
@ -296,7 +283,7 @@ export default function AddDiffusersModel() {
{t('modelManager.addModel')} {t('modelManager.addModel')}
</IAIButton> </IAIButton>
</VStack> </VStack>
</form> </IAIForm>
)} )}
</Formik> </Formik>
</Flex> </Flex>

View File

@ -14,7 +14,7 @@ import {
import IAIButton from 'common/components/IAIButton'; import IAIButton from 'common/components/IAIButton';
import { FaPlus } from 'react-icons/fa'; import { FaArrowLeft, FaPlus } from 'react-icons/fa';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -23,6 +23,7 @@ import type { RootState } from 'app/store';
import { setAddNewModelUIOption } from 'features/ui/store/uiSlice'; import { setAddNewModelUIOption } from 'features/ui/store/uiSlice';
import AddCheckpointModel from './AddCheckpointModel'; import AddCheckpointModel from './AddCheckpointModel';
import AddDiffusersModel from './AddDiffusersModel'; import AddDiffusersModel from './AddDiffusersModel';
import IAIIconButton from 'common/components/IAIIconButton';
function AddModelBox({ function AddModelBox({
text, text,
@ -83,8 +84,22 @@ export default function AddModel() {
closeOnOverlayClick={false} closeOnOverlayClick={false}
> >
<ModalOverlay /> <ModalOverlay />
<ModalContent margin="auto" paddingInlineEnd={4}> <ModalContent margin="auto">
<ModalHeader>{t('modelManager.addNewModel')}</ModalHeader> <ModalHeader>{t('modelManager.addNewModel')} </ModalHeader>
{addNewModelUIOption !== null && (
<IAIIconButton
aria-label={t('common.back')}
tooltip={t('common.back')}
onClick={() => dispatch(setAddNewModelUIOption(null))}
position="absolute"
variant="ghost"
zIndex={1}
size="sm"
insetInlineEnd={12}
top={2}
icon={<FaArrowLeft />}
/>
)}
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>
{addNewModelUIOption == null && ( {addNewModelUIOption == null && (

View File

@ -28,6 +28,7 @@ import { isEqual, pickBy } from 'lodash';
import ModelConvert from './ModelConvert'; import ModelConvert from './ModelConvert';
import IAIFormHelperText from 'common/components/IAIForms/IAIFormHelperText'; import IAIFormHelperText from 'common/components/IAIForms/IAIFormHelperText';
import IAIFormErrorMessage from 'common/components/IAIForms/IAIFormErrorMessage'; import IAIFormErrorMessage from 'common/components/IAIForms/IAIFormErrorMessage';
import IAIForm from 'common/components/IAIForm';
const selector = createSelector( const selector = createSelector(
[systemSelector], [systemSelector],
@ -120,7 +121,7 @@ export default function CheckpointModelEdit() {
onSubmit={editModelFormSubmitHandler} onSubmit={editModelFormSubmitHandler}
> >
{({ handleSubmit, errors, touched }) => ( {({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}> <IAIForm onSubmit={handleSubmit}>
<VStack rowGap={2} alignItems="start"> <VStack rowGap={2} alignItems="start">
{/* Description */} {/* Description */}
<FormControl <FormControl
@ -317,7 +318,7 @@ export default function CheckpointModelEdit() {
{t('modelManager.updateModel')} {t('modelManager.updateModel')}
</IAIButton> </IAIButton>
</VStack> </VStack>
</form> </IAIForm>
)} )}
</Formik> </Formik>
</Flex> </Flex>

View File

@ -18,6 +18,7 @@ import type { RootState } from 'app/store';
import { isEqual, pickBy } from 'lodash'; import { isEqual, pickBy } from 'lodash';
import IAIFormHelperText from 'common/components/IAIForms/IAIFormHelperText'; import IAIFormHelperText from 'common/components/IAIForms/IAIFormHelperText';
import IAIFormErrorMessage from 'common/components/IAIForms/IAIFormErrorMessage'; import IAIFormErrorMessage from 'common/components/IAIForms/IAIFormErrorMessage';
import IAIForm from 'common/components/IAIForm';
const selector = createSelector( const selector = createSelector(
[systemSelector], [systemSelector],
@ -116,7 +117,7 @@ export default function DiffusersModelEdit() {
onSubmit={editModelFormSubmitHandler} onSubmit={editModelFormSubmitHandler}
> >
{({ handleSubmit, errors, touched }) => ( {({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}> <IAIForm onSubmit={handleSubmit}>
<VStack rowGap={2} alignItems="start"> <VStack rowGap={2} alignItems="start">
{/* Description */} {/* Description */}
<FormControl <FormControl
@ -259,7 +260,7 @@ export default function DiffusersModelEdit() {
{t('modelManager.updateModel')} {t('modelManager.updateModel')}
</IAIButton> </IAIButton>
</VStack> </VStack>
</form> </IAIForm>
)} )}
</Formik> </Formik>
</Flex> </Flex>

View File

@ -12,14 +12,13 @@ import {
RadioGroup, RadioGroup,
Spacer, Spacer,
Text, Text,
VStack,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { systemSelector } from 'features/system/store/systemSelectors'; import { systemSelector } from 'features/system/store/systemSelectors';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FaPlus, FaSearch } from 'react-icons/fa'; import { FaSearch, FaTrash } from 'react-icons/fa';
import { addNewModel, searchForModels } from 'app/socketio/actions'; import { addNewModel, searchForModels } from 'app/socketio/actions';
import { import {
@ -34,7 +33,7 @@ import IAIInput from 'common/components/IAIInput';
import { Field, Formik } from 'formik'; import { Field, Formik } from 'formik';
import { forEach, remove } from 'lodash'; import { forEach, remove } from 'lodash';
import type { ChangeEvent, ReactNode } from 'react'; import type { ChangeEvent, ReactNode } from 'react';
import { BiReset } from 'react-icons/bi'; import IAIForm from 'common/components/IAIForm';
const existingModelsSelector = createSelector([systemSelector], (system) => { const existingModelsSelector = createSelector([systemSelector], (system) => {
const { model_list } = system; const { model_list } = system;
@ -71,36 +70,32 @@ function SearchModelEntry({
}; };
return ( return (
<VStack> <Flex
<Flex flexDirection="column"
flexDirection="column" gap={2}
gap={2} backgroundColor={
backgroundColor={ modelsToAdd.includes(model.name) ? 'accent.650' : 'base.800'
modelsToAdd.includes(model.name) ? 'accent.650' : 'base.800' }
} paddingX={4}
paddingX={4} paddingY={2}
paddingY={2} borderRadius={4}
borderRadius={4} >
> <Flex gap={4} alignItems="center" justifyContent="space-between">
<Flex gap={4}> <IAICheckbox
<IAICheckbox value={model.name}
value={model.name} label={<Text fontWeight={500}>{model.name}</Text>}
label={<Text fontWeight={500}>{model.name}</Text>} isChecked={modelsToAdd.includes(model.name)}
isChecked={modelsToAdd.includes(model.name)} isDisabled={existingModels.includes(model.location)}
isDisabled={existingModels.includes(model.location)} onChange={foundModelsChangeHandler}
onChange={foundModelsChangeHandler} ></IAICheckbox>
></IAICheckbox> {existingModels.includes(model.location) && (
{existingModels.includes(model.location) && ( <Badge colorScheme="accent">{t('modelManager.modelExists')}</Badge>
<Badge colorScheme="accent" paddingX={2} paddingY={1}> )}
{t('modelManager.modelExists')}
</Badge>
)}
</Flex>
<Text fontStyle="italic" variant="subtext">
{model.location}
</Text>
</Flex> </Flex>
</VStack> <Text fontStyle="italic" variant="subtext">
{model.location}
</Text>
</Flex>
); );
} }
@ -247,26 +242,26 @@ export default function SearchModels() {
<Text <Text
sx={{ sx={{
fontWeight: 500, fontWeight: 500,
fontSize: 'sm',
}} }}
variant="subtext" variant="subtext"
> >
{t('modelManager.checkpointFolder')} {t('modelManager.checkpointFolder')}
</Text> </Text>
<Text sx={{ fontWeight: 500, fontSize: 'sm' }}>{searchFolder}</Text> <Text sx={{ fontWeight: 500 }}>{searchFolder}</Text>
</Flex> </Flex>
<Spacer /> <Spacer />
<IAIIconButton <IAIIconButton
aria-label={t('modelManager.scanAgain')} aria-label={t('modelManager.scanAgain')}
tooltip={t('modelManager.scanAgain')} tooltip={t('modelManager.scanAgain')}
icon={<BiReset />} icon={<FaSearch />}
fontSize={18} fontSize={18}
disabled={isProcessing} disabled={isProcessing}
onClick={() => dispatch(searchForModels(searchFolder))} onClick={() => dispatch(searchForModels(searchFolder))}
/> />
<IAIIconButton <IAIIconButton
aria-label={t('modelManager.clearCheckpointFolder')} aria-label={t('modelManager.clearCheckpointFolder')}
icon={<FaPlus style={{ transform: 'rotate(45deg)' }} />} tooltip={t('modelManager.clearCheckpointFolder')}
icon={<FaTrash />}
onClick={resetSearchModelHandler} onClick={resetSearchModelHandler}
/> />
</Flex> </Flex>
@ -278,9 +273,9 @@ export default function SearchModels() {
}} }}
> >
{({ handleSubmit }) => ( {({ handleSubmit }) => (
<form onSubmit={handleSubmit}> <IAIForm onSubmit={handleSubmit} width="100%">
<HStack columnGap={2} alignItems="flex-end" width="100%"> <HStack columnGap={2} alignItems="flex-end">
<FormControl isRequired width="lg"> <FormControl flexGrow={1}>
<Field <Field
as={IAIInput} as={IAIInput}
id="checkpointFolder" id="checkpointFolder"
@ -296,12 +291,12 @@ export default function SearchModels() {
tooltip={t('modelManager.findModels')} tooltip={t('modelManager.findModels')}
type="submit" type="submit"
disabled={isProcessing} disabled={isProcessing}
paddingX={10} px={8}
> >
{t('modelManager.findModels')} {t('modelManager.findModels')}
</IAIButton> </IAIButton>
</HStack> </HStack>
</form> </IAIForm>
)} )}
</Formik> </Formik>
)} )}
@ -412,7 +407,6 @@ export default function SearchModels() {
maxHeight={72} maxHeight={72}
overflowY="scroll" overflowY="scroll"
borderRadius="sm" borderRadius="sm"
paddingInlineEnd={4}
gap={2} gap={2}
> >
{foundModels.length > 0 ? ( {foundModels.length > 0 ? (