1
0
Fork 0

feat(gui): move model controls into each tab

This commit is contained in:
Sean Sube 2023-07-21 17:38:01 -05:00
parent 27a21dfa62
commit f14f197264
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
17 changed files with 686 additions and 539 deletions

View File

@ -140,9 +140,7 @@ export interface UpscaleParams {
/** /**
* Parameters for upscale requests. * Parameters for upscale requests.
*/ */
export interface UpscaleReqParams { export interface UpscaleReqParams extends BaseImgParams {
prompt: string;
negativePrompt?: string;
source: Blob; source: Blob;
} }

View File

@ -8,7 +8,6 @@ import { useHash } from 'react-use/lib/useHash';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { StateContext } from '../state.js'; import { StateContext } from '../state.js';
import { ModelControl } from './control/ModelControl.js';
import { ImageHistory } from './ImageHistory.js'; import { ImageHistory } from './ImageHistory.js';
import { Logo } from './Logo.js'; import { Logo } from './Logo.js';
import { Blend } from './tab/Blend.js'; import { Blend } from './tab/Blend.js';
@ -43,9 +42,6 @@ export function OnnxWeb() {
<Box sx={{ my: 4 }}> <Box sx={{ my: 4 }}>
<Logo /> <Logo />
</Box> </Box>
<Box sx={{ mx: 4, my: 4 }}>
<ModelControl />
</Box>
<TabContext value={getTab(hash)}> <TabContext value={getTab(hash)}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}> <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<TabList onChange={(_e, idx) => { <TabList onChange={(_e, idx) => {

View File

@ -20,29 +20,35 @@ import { useContext } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { BaseImgParams, Txt2ImgParams } from '../client/types.js'; import { BaseImgParams, HighresParams, Txt2ImgParams, UpscaleParams } from '../client/types.js';
import { StateContext } from '../state.js'; import { StateContext } from '../state.js';
const { useState, Fragment } = React;
export interface ProfilesProps { export interface ProfilesProps {
highres: HighresParams;
params: BaseImgParams; params: BaseImgParams;
setParams: ((params: BaseImgParams) => void) | undefined; upscale: UpscaleParams;
setHighres(params: HighresParams): void;
setParams(params: BaseImgParams): void;
setUpscale(params: UpscaleParams): void;
} }
export function Profiles(props: ProfilesProps) { export function Profiles(props: ProfilesProps) {
const state = mustExist(useContext(StateContext)); const state = mustExist(useContext(StateContext));
const profiles = useStore(state, (s) => s.profiles);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const saveProfile = useStore(state, (s) => s.saveProfile); const saveProfile = useStore(state, (s) => s.saveProfile);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const removeProfile = useStore(state, (s) => s.removeProfile); const removeProfile = useStore(state, (s) => s.removeProfile);
const profiles = useStore(state, (s) => s.profiles);
const highres = useStore(state, (s) => s.highres); const [dialogOpen, setDialogOpen] = useState(false);
const upscale = useStore(state, (s) => s.upscale); const [profileName, setProfileName] = useState('');
const [dialogOpen, setDialogOpen] = React.useState(false);
const [profileName, setProfileName] = React.useState('');
const { t } = useTranslation(); const { t } = useTranslation();
return <> return <Stack direction='row' spacing={2}>
<Autocomplete <Autocomplete
id="profile-select" id="profile-select"
options={profiles} options={profiles}
@ -77,36 +83,10 @@ export function Profiles(props: ProfilesProps) {
<Button type="button" variant="contained" onClick={() => setDialogOpen(true)}> <Button type="button" variant="contained" onClick={() => setDialogOpen(true)}>
<SaveIcon /> <SaveIcon />
</Button> </Button>
<Button component='label' variant="contained">
<ImageSearch />
<input
hidden
accept={'.json,.jpg,.jpeg,.png,.txt,.webp'}
type='file'
onChange={(event) => {
const { files } = event.target;
if (doesExist(files) && files.length > 0) {
const file = mustExist(files[0]);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
loadParamsFromFile(file).then((newParams) => {
if (doesExist(props.setParams) && doesExist(newParams)) {
props.setParams({
...props.params,
...newParams,
});
}
});
}
}}
onClick={(event) => {
event.currentTarget.value = '';
}}
/>
</Button>
</Stack> </Stack>
)} )}
onChange={(event, value) => { onChange={(event, value) => {
if (doesExist(value) && doesExist(props.setParams)) { if (doesExist(value)) {
props.setParams({ props.setParams({
...value.params ...value.params
}); });
@ -138,8 +118,8 @@ export function Profiles(props: ProfilesProps) {
saveProfile({ saveProfile({
params: props.params, params: props.params,
name: profileName, name: profileName,
highResParams: highres, highResParams: props.highres,
upscaleParams: upscale, upscaleParams: props.upscale,
}); });
setDialogOpen(false); setDialogOpen(false);
setProfileName(''); setProfileName('');
@ -147,7 +127,33 @@ export function Profiles(props: ProfilesProps) {
>{t('profile.save')}</Button> >{t('profile.save')}</Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
</>; <Button component='label' variant="contained">
<ImageSearch />
<input
hidden
accept={'.json,.jpg,.jpeg,.png,.txt,.webp'}
type='file'
onChange={(event) => {
const { files } = event.target;
if (doesExist(files) && files.length > 0) {
const file = mustExist(files[0]);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
loadParamsFromFile(file).then((newParams) => {
if (doesExist(newParams)) {
props.setParams({
...props.params,
...newParams,
});
}
});
}
}}
onClick={(event) => {
event.currentTarget.value = '';
}}
/>
</Button>
</Stack>;
} }
export async function loadParamsFromFile(file: File): Promise<Partial<Txt2ImgParams>> { export async function loadParamsFromFile(file: File): Promise<Partial<Txt2ImgParams>> {
@ -276,7 +282,7 @@ export async function parseAutoComment(comment: string): Promise<Partial<Txt2Img
} }
break; break;
default: default:
// unknown param // unknown param
} }
} }

View File

@ -38,7 +38,7 @@ export function ImageCard(props: ImageCardProps) {
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setInpaint = useStore(state, (s) => s.setInpaint); const setInpaint = useStore(state, (s) => s.setInpaint);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setUpscale = useStore(state, (s) => s.setUpscaleTab); const setUpscale = useStore(state, (s) => s.setUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setBlend = useStore(state, (s) => s.setBlend); const setBlend = useStore(state, (s) => s.setBlend);

View File

@ -3,17 +3,21 @@ import { Checkbox, FormControl, FormControlLabel, InputLabel, MenuItem, Select,
import * as React from 'react'; import * as React from 'react';
import { useContext } from 'react'; import { useContext } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand';
import { ConfigContext, StateContext } from '../../state.js'; import { HighresParams } from '../../client/types.js';
import { ConfigContext } from '../../state.js';
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
export function HighresControl() { export interface HighresControlProps {
const { params } = mustExist(useContext(ConfigContext)); highres: HighresParams;
const state = mustExist(useContext(StateContext)); setHighres(params: Partial<HighresParams>): void;
const highres = useStore(state, (s) => s.highres); }
export function HighresControl(props: HighresControlProps) {
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setHighres = useStore(state, (s) => s.setHighres); const { highres, setHighres } = props;
const { params } = mustExist(useContext(ConfigContext));
const { t } = useTranslation(); const { t } = useTranslation();
return <Stack direction='row' spacing={4}> return <Stack direction='row' spacing={4}>
@ -22,7 +26,7 @@ export function HighresControl() {
control={<Checkbox control={<Checkbox
checked={highres.enabled} checked={highres.enabled}
value='check' value='check'
onChange={(event) => { onChange={(_event) => {
setHighres({ setHighres({
enabled: highres.enabled === false, enabled: highres.enabled === false,
}); });

View File

@ -13,21 +13,21 @@ import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../sta
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
import { PromptInput } from '../input/PromptInput.js'; import { PromptInput } from '../input/PromptInput.js';
import { QueryList } from '../input/QueryList.js'; import { QueryList } from '../input/QueryList.js';
import { Profiles } from '../Profiles.js';
export interface ImageControlProps { export interface ImageControlProps {
selector: (state: OnnxState) => BaseImgParams; onChange(params: BaseImgParams): void;
selector(state: OnnxState): BaseImgParams;
onChange?: (params: BaseImgParams) => void;
} }
/** /**
* Doesn't need to use state directly, the parent component knows which params to pass * Doesn't need to use state directly, the parent component knows which params to pass
*/ */
export function ImageControl(props: ImageControlProps) { export function ImageControl(props: ImageControlProps) {
// eslint-disable-next-line @typescript-eslint/unbound-method
const { onChange, selector } = props;
const { params } = mustExist(useContext(ConfigContext)); const { params } = mustExist(useContext(ConfigContext));
const state = mustExist(useContext(StateContext)); const state = mustExist(useContext(StateContext));
const controlState = useStore(state, props.selector); const controlState = useStore(state, selector);
const { t } = useTranslation(); const { t } = useTranslation();
const client = mustExist(useContext(ClientContext)); const client = mustExist(useContext(ClientContext));
@ -40,7 +40,6 @@ export function ImageControl(props: ImageControlProps) {
return <Stack spacing={2}> return <Stack spacing={2}>
<Stack direction='row' spacing={4}> <Stack direction='row' spacing={4}>
<Profiles params={controlState} setParams={props.onChange} />
<QueryList <QueryList
id='schedulers' id='schedulers'
labelKey='scheduler' labelKey='scheduler'
@ -50,8 +49,8 @@ export function ImageControl(props: ImageControlProps) {
}} }}
value={mustDefault(controlState.scheduler, '')} value={mustDefault(controlState.scheduler, '')}
onChange={(value) => { onChange={(value) => {
if (doesExist(props.onChange)) { if (doesExist(onChange)) {
props.onChange({ onChange({
...controlState, ...controlState,
scheduler: value, scheduler: value,
}); });
@ -66,8 +65,8 @@ export function ImageControl(props: ImageControlProps) {
step={params.eta.step} step={params.eta.step}
value={controlState.eta} value={controlState.eta}
onChange={(eta) => { onChange={(eta) => {
if (doesExist(props.onChange)) { if (doesExist(onChange)) {
props.onChange({ onChange({
...controlState, ...controlState,
eta, eta,
}); });
@ -82,8 +81,8 @@ export function ImageControl(props: ImageControlProps) {
step={params.cfg.step} step={params.cfg.step}
value={controlState.cfg} value={controlState.cfg}
onChange={(cfg) => { onChange={(cfg) => {
if (doesExist(props.onChange)) { if (doesExist(onChange)) {
props.onChange({ onChange({
...controlState, ...controlState,
cfg, cfg,
}); });
@ -97,12 +96,10 @@ export function ImageControl(props: ImageControlProps) {
step={params.steps.step} step={params.steps.step}
value={controlState.steps} value={controlState.steps}
onChange={(steps) => { onChange={(steps) => {
if (doesExist(props.onChange)) { onChange({
props.onChange({ ...controlState,
...controlState, steps,
steps, });
});
}
}} }}
/> />
<NumericField <NumericField
@ -112,12 +109,10 @@ export function ImageControl(props: ImageControlProps) {
step={params.seed.step} step={params.seed.step}
value={controlState.seed} value={controlState.seed}
onChange={(seed) => { onChange={(seed) => {
if (doesExist(props.onChange)) { onChange({
props.onChange({ ...controlState,
...controlState, seed,
seed, });
});
}
}} }}
/> />
<Button <Button
@ -125,12 +120,10 @@ export function ImageControl(props: ImageControlProps) {
startIcon={<Casino />} startIcon={<Casino />}
onClick={() => { onClick={() => {
const seed = Math.floor(Math.random() * params.seed.max); const seed = Math.floor(Math.random() * params.seed.max);
if (doesExist(props.onChange)) { props.onChange({
props.onChange({ ...controlState,
...controlState, seed,
seed, });
});
}
}} }}
> >
{t('parameter.newSeed')} {t('parameter.newSeed')}
@ -144,12 +137,10 @@ export function ImageControl(props: ImageControlProps) {
step={params.batch.step} step={params.batch.step}
value={controlState.batch} value={controlState.batch}
onChange={(batch) => { onChange={(batch) => {
if (doesExist(props.onChange)) { props.onChange({
props.onChange({ ...controlState,
...controlState, batch,
batch, });
});
}
}} }}
/> />
<NumericField <NumericField
@ -159,12 +150,10 @@ export function ImageControl(props: ImageControlProps) {
step={params.tiles.step} step={params.tiles.step}
value={controlState.tiles} value={controlState.tiles}
onChange={(tiles) => { onChange={(tiles) => {
if (doesExist(props.onChange)) { props.onChange({
props.onChange({ ...controlState,
...controlState, tiles,
tiles, });
});
}
}} }}
/> />
<NumericField <NumericField
@ -175,12 +164,10 @@ export function ImageControl(props: ImageControlProps) {
step={params.overlap.step} step={params.overlap.step}
value={controlState.overlap} value={controlState.overlap}
onChange={(overlap) => { onChange={(overlap) => {
if (doesExist(props.onChange)) { props.onChange({
props.onChange({ ...controlState,
...controlState, overlap,
overlap, });
});
}
}} }}
/> />
<NumericField <NumericField
@ -190,12 +177,10 @@ export function ImageControl(props: ImageControlProps) {
step={params.stride.step} step={params.stride.step}
value={controlState.stride} value={controlState.stride}
onChange={(stride) => { onChange={(stride) => {
if (doesExist(props.onChange)) { props.onChange({
props.onChange({ ...controlState,
...controlState, stride,
stride, });
});
}
}} }}
/> />
<FormControlLabel <FormControlLabel
@ -204,12 +189,10 @@ export function ImageControl(props: ImageControlProps) {
checked={controlState.tiledVAE} checked={controlState.tiledVAE}
value='check' value='check'
onChange={(event) => { onChange={(event) => {
if (doesExist(props.onChange)) { props.onChange({
props.onChange({ ...controlState,
...controlState, tiledVAE: controlState.tiledVAE === false,
tiledVAE: controlState.tiledVAE === false, });
});
}
}} }}
/>} />}
/> />
@ -218,12 +201,10 @@ export function ImageControl(props: ImageControlProps) {
prompt={controlState.prompt} prompt={controlState.prompt}
negativePrompt={controlState.negativePrompt} negativePrompt={controlState.negativePrompt}
onChange={(value) => { onChange={(value) => {
if (doesExist(props.onChange)) { props.onChange({
props.onChange({ ...controlState,
...controlState, ...value,
...value, });
});
}
}} }}
/> />
</Stack>; </Stack>;

View File

@ -4,25 +4,25 @@ import { useMutation, useQuery } from '@tanstack/react-query';
import * as React from 'react'; import * as React from 'react';
import { useContext } from 'react'; import { useContext } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useHash } from 'react-use/lib/useHash';
import { useStore } from 'zustand';
import { ModelParams } from '../../client/types.js';
import { STALE_TIME } from '../../config.js'; import { STALE_TIME } from '../../config.js';
import { ClientContext, StateContext } from '../../state.js'; import { ClientContext, StateContext } from '../../state.js';
import { QueryList } from '../input/QueryList.js'; import { QueryList } from '../input/QueryList.js';
import { QueryMenu } from '../input/QueryMenu.js';
import { getTab } from '../utils.js';
export function ModelControl() { export interface ModelControlProps {
model: ModelParams;
setModel(params: Partial<ModelParams>): void;
}
export function ModelControl(props: ModelControlProps) {
// eslint-disable-next-line @typescript-eslint/unbound-method
const { model, setModel } = props;
const client = mustExist(useContext(ClientContext)); const client = mustExist(useContext(ClientContext));
const state = mustExist(useContext(StateContext)); const state = mustExist(useContext(StateContext));
const params = useStore(state, (s) => s.model);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setModel = useStore(state, (s) => s.setModel);
const { t } = useTranslation(); const { t } = useTranslation();
const [hash, _setHash] = useHash();
const restart = useMutation(['restart'], async () => client.restart()); const restart = useMutation(['restart'], async () => client.restart());
const models = useQuery(['models'], async () => client.models(), { const models = useQuery(['models'], async () => client.models(), {
staleTime: STALE_TIME, staleTime: STALE_TIME,
@ -34,135 +34,83 @@ export function ModelControl() {
staleTime: STALE_TIME, staleTime: STALE_TIME,
}); });
function addToken(type: string, name: string, weight = 1.0) { return <Stack direction='row' spacing={2}>
const tab = getTab(hash); <QueryList
const current = state.getState(); id='platforms'
labelKey='platform'
switch (tab) { name={t('parameter.platform')}
case 'txt2img': { query={{
const { prompt } = current.txt2img; result: platforms,
current.setTxt2Img({ }}
prompt: `<${type}:${name}:1.0> ${prompt}`, value={model.platform}
onChange={(platform) => {
setModel({
platform,
}); });
break; }}
} />
case 'img2img': { <QueryList
const { prompt } = current.img2img; id='pipeline'
current.setImg2Img({ labelKey='pipeline'
prompt: `<${type}:${name}:1.0> ${prompt}`, name={t('parameter.pipeline')}
query={{
result: pipelines,
}}
value={model.pipeline}
onChange={(pipeline) => {
setModel({
pipeline,
}); });
break; }}
} />
default: <QueryList
// not supported yet id='diffusion'
} labelKey='model'
} name={t('modelType.diffusion', { count: 1 })}
query={{
return <Stack direction='column' spacing={2}> result: models,
<Stack direction='row' spacing={2}> selector: (result) => result.diffusion,
<QueryList }}
id='platforms' value={model.model}
labelKey='platform' onChange={(newModel) => {
name={t('parameter.platform')} setModel({
query={{ model: newModel,
result: platforms, });
}} }}
value={params.platform} />
onChange={(platform) => { <QueryList
setModel({ id='upscaling'
platform, labelKey='model'
}); name={t('modelType.upscaling', { count: 1 })}
}} query={{
/> result: models,
<QueryList selector: (result) => result.upscaling,
id='pipeline' }}
labelKey='pipeline' value={model.upscaling}
name={t('parameter.pipeline')} onChange={(upscaling) => {
query={{ setModel({
result: pipelines, upscaling,
}} });
value={params.pipeline} }}
onChange={(pipeline) => { />
setModel({ <QueryList
pipeline, id='correction'
}); labelKey='model'
}} name={t('modelType.correction', { count: 1 })}
/> query={{
<QueryList result: models,
id='diffusion' selector: (result) => result.correction,
labelKey='model' }}
name={t('modelType.diffusion', {count: 1})} value={model.correction}
query={{ onChange={(correction) => {
result: models, setModel({
selector: (result) => result.diffusion, correction,
}} });
value={params.model} }}
onChange={(model) => { />
setModel({ <Button
model, variant='outlined'
}); onClick={() => restart.mutate()}
}} >{t('admin.restart')}</Button>
/>
<QueryList
id='upscaling'
labelKey='model'
name={t('modelType.upscaling', {count: 1})}
query={{
result: models,
selector: (result) => result.upscaling,
}}
value={params.upscaling}
onChange={(upscaling) => {
setModel({
upscaling,
});
}}
/>
<QueryList
id='correction'
labelKey='model'
name={t('modelType.correction', {count: 1})}
query={{
result: models,
selector: (result) => result.correction,
}}
value={params.correction}
onChange={(correction) => {
setModel({
correction,
});
}}
/>
</Stack>
<Stack direction='row' spacing={2}>
<QueryMenu
id='inversion'
labelKey='model.inversion'
name={t('modelType.inversion')}
query={{
result: models,
selector: (result) => result.networks.filter((network) => network.type === 'inversion').map((network) => network.name),
}}
onSelect={(name) => {
addToken('inversion', name);
}}
/>
<QueryMenu
id='lora'
labelKey='model.lora'
name={t('modelType.lora')}
query={{
result: models,
selector: (result) => result.networks.filter((network) => network.type === 'lora').map((network) => network.name),
}}
onSelect={(name) => {
addToken('lora', name);
}}
/>
<Button
variant='outlined'
onClick={() => restart.mutate()}
>{t('admin.restart')}</Button>
</Stack>
</Stack>; </Stack>;
} }

View File

@ -3,17 +3,21 @@ import { Checkbox, FormControl, FormControlLabel, InputLabel, MenuItem, Select,
import * as React from 'react'; import * as React from 'react';
import { useContext } from 'react'; import { useContext } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand';
import { ConfigContext, StateContext } from '../../state.js'; import { UpscaleParams } from '../../client/types.js';
import { ConfigContext } from '../../state.js';
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
export function UpscaleControl() { export interface UpscaleControlProps {
const { params } = mustExist(useContext(ConfigContext)); upscale: UpscaleParams;
const state = mustExist(useContext(StateContext)); setUpscale(params: Partial<UpscaleParams>): void;
const upscale = useStore(state, (s) => s.upscale); }
export function UpscaleControl(props: UpscaleControlProps) {
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setUpscale = useStore(state, (s) => s.setUpscale); const { upscale, setUpscale } = props;
const { params } = mustExist(useContext(ConfigContext));
const { t } = useTranslation(); const { t } = useTranslation();
return <Stack direction='row' spacing={4}> return <Stack direction='row' spacing={4}>
@ -22,7 +26,7 @@ export function UpscaleControl() {
control={<Checkbox control={<Checkbox
checked={upscale.enabled} checked={upscale.enabled}
value='check' value='check'
onChange={(event) => { onChange={(_event) => {
setUpscale({ setUpscale({
enabled: upscale.enabled === false, enabled: upscale.enabled === false,
}); });

View File

@ -4,8 +4,8 @@ import { Button, Stack, Typography } from '@mui/material';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import React, { RefObject, useContext, useEffect, useMemo, useRef } from 'react'; import React, { RefObject, useContext, useEffect, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useStore } from 'zustand';
import { BrushParams } from '../../client/types.js';
import { SAVE_TIME } from '../../config.js'; import { SAVE_TIME } from '../../config.js';
import { ConfigContext, LoggerContext, StateContext } from '../../state.js'; import { ConfigContext, LoggerContext, StateContext } from '../../state.js';
import { imageFromBlob } from '../../utils.js'; import { imageFromBlob } from '../../utils.js';
@ -36,14 +36,17 @@ export interface Point {
} }
export interface MaskCanvasProps { export interface MaskCanvasProps {
brush: BrushParams;
source?: Maybe<Blob>; source?: Maybe<Blob>;
mask?: Maybe<Blob>; mask?: Maybe<Blob>;
onSave: (blob: Blob) => void; onSave(blob: Blob): void;
setBrush(brush: Partial<BrushParams>): void;
} }
export function MaskCanvas(props: MaskCanvasProps) { export function MaskCanvas(props: MaskCanvasProps) {
const { source, mask } = props; // eslint-disable-next-line @typescript-eslint/unbound-method
const { source, mask, brush, setBrush } = props;
const { params } = mustExist(useContext(ConfigContext)); const { params } = mustExist(useContext(ConfigContext));
const logger = mustExist(useContext(LoggerContext)); const logger = mustExist(useContext(LoggerContext));
@ -202,9 +205,6 @@ export function MaskCanvas(props: MaskCanvasProps) {
}); });
const state = mustExist(useContext(StateContext)); const state = mustExist(useContext(StateContext));
const brush = useStore(state, (s) => s.brush);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setBrush = useStore(state, (s) => s.setBrush);
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => { useEffect(() => {

View File

@ -1,16 +1,23 @@
import { doesExist, Maybe } from '@apextoaster/js-utils'; import { doesExist, mustExist } from '@apextoaster/js-utils';
import { TextField } from '@mui/material'; import { TextField } from '@mui/material';
import { Stack } from '@mui/system'; import { Stack } from '@mui/system';
import { useQuery } from '@tanstack/react-query';
import * as React from 'react'; import * as React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { QueryMenu } from '../input/QueryMenu.js';
import { STALE_TIME } from '../../config.js';
import { ClientContext } from '../../state.js';
const { useContext } = React;
export interface PromptValue { export interface PromptValue {
prompt: string; prompt: string;
negativePrompt?: string; negativePrompt?: string;
} }
export interface PromptInputProps extends PromptValue { export interface PromptInputProps extends PromptValue {
onChange?: Maybe<(value: PromptValue) => void>; onChange: (value: PromptValue) => void;
} }
export const PROMPT_GROUP = 75; export const PROMPT_GROUP = 75;
@ -29,12 +36,24 @@ export function PromptInput(props: PromptInputProps) {
const tokens = splitPrompt(prompt); const tokens = splitPrompt(prompt);
const groups = Math.ceil(tokens.length / PROMPT_GROUP); const groups = Math.ceil(tokens.length / PROMPT_GROUP);
const client = mustExist(useContext(ClientContext));
const models = useQuery(['models'], async () => client.models(), {
staleTime: STALE_TIME,
});
const { t } = useTranslation(); const { t } = useTranslation();
const helper = t('input.prompt.tokens', { const helper = t('input.prompt.tokens', {
groups, groups,
tokens: tokens.length, tokens: tokens.length,
}); });
function addToken(type: string, name: string, weight = 1.0) {
props.onChange({
prompt: `<${type}:${name}:1.0> ${prompt}`,
negativePrompt,
});
}
return <Stack spacing={2}> return <Stack spacing={2}>
<TextField <TextField
label={t('parameter.prompt')} label={t('parameter.prompt')}
@ -63,5 +82,31 @@ export function PromptInput(props: PromptInputProps) {
} }
}} }}
/> />
<Stack direction='row' spacing={2}>
<QueryMenu
id='inversion'
labelKey='model.inversion'
name={t('modelType.inversion')}
query={{
result: models,
selector: (result) => result.networks.filter((network) => network.type === 'inversion').map((network) => network.name),
}}
onSelect={(name) => {
addToken('inversion', name);
}}
/>
<QueryMenu
id='lora'
labelKey='model.lora'
name={t('modelType.lora')}
query={{
result: models,
selector: (result) => result.networks.filter((network) => network.type === 'lora').map((network) => network.name),
}}
onSelect={(name) => {
addToken('lora', name);
}}
/>
</Stack>
</Stack>; </Stack>;
} }

View File

@ -15,12 +15,12 @@ import { MaskCanvas } from '../input/MaskCanvas.js';
export function Blend() { export function Blend() {
async function uploadSource() { async function uploadSource() {
const { model, blend, upscale } = state.getState(); const { blend, blendModel, blendUpscale } = state.getState();
const { image, retry } = await client.blend(model, { const { image, retry } = await client.blend(blendModel, {
...blend, ...blend,
mask: mustExist(blend.mask), mask: mustExist(blend.mask),
sources: mustExist(blend.sources), // TODO: show an error if this doesn't exist sources: mustExist(blend.sources), // TODO: show an error if this doesn't exist
}, upscale); }, blendUpscale);
pushHistory(image, retry); pushHistory(image, retry);
} }
@ -32,10 +32,16 @@ export function Blend() {
}); });
const state = mustExist(useContext(StateContext)); const state = mustExist(useContext(StateContext));
const brush = useStore(state, (s) => s.blendBrush);
const blend = useStore(state, (s) => s.blend); const blend = useStore(state, (s) => s.blend);
const upscale = useStore(state, (s) => s.blendUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setBlend = useStore(state, (s) => s.setBlend); const setBlend = useStore(state, (s) => s.setBlend);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setBrush = useStore(state, (s) => s.setBlendBrush);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setUpscale = useStore(state, (s) => s.setBlendUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method
const pushHistory = useStore(state, (s) => s.pushHistory); const pushHistory = useStore(state, (s) => s.pushHistory);
const { t } = useTranslation(); const { t } = useTranslation();
@ -61,6 +67,7 @@ export function Blend() {
/> />
)} )}
<MaskCanvas <MaskCanvas
brush={brush}
source={sources[0]} source={sources[0]}
mask={blend.mask} mask={blend.mask}
onSave={(mask) => { onSave={(mask) => {
@ -68,8 +75,9 @@ export function Blend() {
mask, mask,
}); });
}} }}
setBrush={setBrush}
/> />
<UpscaleControl /> <UpscaleControl upscale={upscale} setUpscale={setUpscale} />
<Button <Button
disabled={sources.length < 2} disabled={sources.length < 2}
variant='contained' variant='contained'

View File

@ -14,12 +14,13 @@ import { ImageInput } from '../input/ImageInput.js';
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
import { QueryList } from '../input/QueryList.js'; import { QueryList } from '../input/QueryList.js';
import { HighresControl } from '../control/HighresControl.js'; import { HighresControl } from '../control/HighresControl.js';
import { ModelControl } from '../control/ModelControl.js';
import { Profiles } from '../Profiles.js';
export function Img2Img() { export function Img2Img() {
const { params } = mustExist(useContext(ConfigContext)); const { params } = mustExist(useContext(ConfigContext));
async function uploadSource() { async function uploadSource() {
const { model, img2img, upscale, highres } = state.getState();
const { image, retry } = await client.img2img(model, { const { image, retry } = await client.img2img(model, {
...img2img, ...img2img,
source: mustExist(img2img.source), // TODO: show an error if this doesn't exist source: mustExist(img2img.source), // TODO: show an error if this doesn't exist
@ -42,21 +43,27 @@ export function Img2Img() {
}); });
const state = mustExist(useContext(StateContext)); const state = mustExist(useContext(StateContext));
const control = useStore(state, (s) => s.model.control); const model = useStore(state, (s) => s.img2imgModel);
const source = useStore(state, (s) => s.img2img.source); const source = useStore(state, (s) => s.img2img.source);
const sourceFilter = useStore(state, (s) => s.img2img.sourceFilter); const img2img = useStore(state, (s) => s.img2img);
const strength = useStore(state, (s) => s.img2img.strength); const highres = useStore(state, (s) => s.img2imgHighres);
const loopback = useStore(state, (s) => s.img2img.loopback); const upscale = useStore(state, (s) => s.img2imgUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setImg2Img = useStore(state, (s) => s.setImg2Img); const setImg2Img = useStore(state, (s) => s.setImg2Img);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setModel = useStore(state, (s) => s.setModel); const setHighres = useStore(state, (s) => s.setImg2ImgHighres);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setUpscale = useStore(state, (s) => s.setImg2ImgUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setModel = useStore(state, (s) => s.setImg2ImgModel);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const pushHistory = useStore(state, (s) => s.pushHistory); const pushHistory = useStore(state, (s) => s.pushHistory);
const { t } = useTranslation(); const { t } = useTranslation();
return <Box> return <Box>
<Stack spacing={2}> <Stack spacing={2}>
<Profiles params={img2img} setParams={setImg2Img} highres={highres} setHighres={setHighres} upscale={upscale} setUpscale={setUpscale} />
<ModelControl model={model} setModel={setModel} />
<ImageInput <ImageInput
filter={IMAGE_FILTER} filter={IMAGE_FILTER}
image={source} image={source}
@ -77,7 +84,7 @@ export function Img2Img() {
result: models, result: models,
selector: (result) => result.networks.filter((network) => network.type === 'control').map((network) => network.name), selector: (result) => result.networks.filter((network) => network.type === 'control').map((network) => network.name),
}} }}
value={control} value={model.control}
onChange={(newControl) => { onChange={(newControl) => {
setModel({ setModel({
control: newControl, control: newControl,
@ -93,7 +100,7 @@ export function Img2Img() {
selector: (f) => f.source, selector: (f) => f.source,
}} }}
showNone showNone
value={sourceFilter} value={img2img.sourceFilter}
onChange={(newFilter) => { onChange={(newFilter) => {
setImg2Img({ setImg2Img({
sourceFilter: newFilter, sourceFilter: newFilter,
@ -106,7 +113,7 @@ export function Img2Img() {
min={params.strength.min} min={params.strength.min}
max={params.strength.max} max={params.strength.max}
step={params.strength.step} step={params.strength.step}
value={strength} value={img2img.strength}
onChange={(value) => { onChange={(value) => {
setImg2Img({ setImg2Img({
strength: value, strength: value,
@ -118,7 +125,7 @@ export function Img2Img() {
min={params.loopback.min} min={params.loopback.min}
max={params.loopback.max} max={params.loopback.max}
step={params.loopback.step} step={params.loopback.step}
value={loopback} value={img2img.loopback}
onChange={(value) => { onChange={(value) => {
setImg2Img({ setImg2Img({
loopback: value, loopback: value,
@ -126,8 +133,8 @@ export function Img2Img() {
}} }}
/> />
</Stack> </Stack>
<HighresControl /> <HighresControl highres={highres} setHighres={setHighres} />
<UpscaleControl /> <UpscaleControl upscale={upscale} setUpscale={setUpscale} />
<Button <Button
disabled={doesExist(source) === false} disabled={doesExist(source) === false}
variant='contained' variant='contained'

View File

@ -1,21 +1,23 @@
import { doesExist, mustExist } from '@apextoaster/js-utils'; import { doesExist, mustExist } from '@apextoaster/js-utils';
import { Alert, Box, Button, FormControl, FormControlLabel, InputLabel, MenuItem, Select, Stack } from '@mui/material'; import { Alert, Box, Button, FormControl, FormControlLabel, InputLabel, MenuItem, Select, Stack } from '@mui/material';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import * as React from 'react'; import * as React from 'react';
import { useContext } from 'react'; import { useContext } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { IMAGE_FILTER, STALE_TIME } from '../../config.js'; import { IMAGE_FILTER, STALE_TIME } from '../../config.js';
import { ClientContext, ConfigContext, StateContext } from '../../state.js'; import { ClientContext, ConfigContext, StateContext } from '../../state.js';
import { HighresControl } from '../control/HighresControl.js';
import { ImageControl } from '../control/ImageControl.js'; import { ImageControl } from '../control/ImageControl.js';
import { ModelControl } from '../control/ModelControl.js';
import { OutpaintControl } from '../control/OutpaintControl.js'; import { OutpaintControl } from '../control/OutpaintControl.js';
import { UpscaleControl } from '../control/UpscaleControl.js'; import { UpscaleControl } from '../control/UpscaleControl.js';
import { ImageInput } from '../input/ImageInput.js'; import { ImageInput } from '../input/ImageInput.js';
import { MaskCanvas } from '../input/MaskCanvas.js'; import { MaskCanvas } from '../input/MaskCanvas.js';
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
import { QueryList } from '../input/QueryList.js'; import { QueryList } from '../input/QueryList.js';
import { HighresControl } from '../control/HighresControl.js'; import { Profiles } from '../Profiles.js';
export function Inpaint() { export function Inpaint() {
const { params } = mustExist(useContext(ConfigContext)); const { params } = mustExist(useContext(ConfigContext));
@ -29,9 +31,6 @@ export function Inpaint() {
}); });
async function uploadSource(): Promise<void> { async function uploadSource(): Promise<void> {
// these are not watched by the component, only sent by the mutation
const { model, inpaint, outpaint, upscale, highres } = state.getState();
if (outpaint.enabled) { if (outpaint.enabled) {
const { image, retry } = await client.outpaint(model, { const { image, retry } = await client.outpaint(model, {
...inpaint, ...inpaint,
@ -57,21 +56,31 @@ export function Inpaint() {
} }
function supportsInpaint(): boolean { function supportsInpaint(): boolean {
return diffusionModel.includes('inpaint'); return model.model.includes('inpaint');
} }
const state = mustExist(useContext(StateContext)); const state = mustExist(useContext(StateContext));
const fillColor = useStore(state, (s) => s.inpaint.fillColor);
const filter = useStore(state, (s) => s.inpaint.filter);
const noise = useStore(state, (s) => s.inpaint.noise);
const mask = useStore(state, (s) => s.inpaint.mask); const mask = useStore(state, (s) => s.inpaint.mask);
const source = useStore(state, (s) => s.inpaint.source); const source = useStore(state, (s) => s.inpaint.source);
const strength = useStore(state, (s) => s.inpaint.strength); const inpaint = useStore(state, (s) => s.inpaint);
const tileOrder = useStore(state, (s) => s.inpaint.tileOrder); const outpaint = useStore(state, (s) => s.outpaint);
const diffusionModel = useStore(state, (s) => s.model.model);
const brush = useStore(state, (s) => s.inpaintBrush);
const highres = useStore(state, (s) => s.inpaintHighres);
const model = useStore(state, (s) => s.inpaintModel);
const upscale = useStore(state, (s) => s.inpaintUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setInpaint = useStore(state, (s) => s.setInpaint); const setInpaint = useStore(state, (s) => s.setInpaint);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setBrush = useStore(state, (s) => s.setInpaintBrush);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setModel = useStore(state, (s) => s.setInpaintModel);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setHighres = useStore(state, (s) => s.setInpaintHighres);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setUpscale = useStore(state, (s) => s.setInpaintUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const pushHistory = useStore(state, (s) => s.pushHistory); const pushHistory = useStore(state, (s) => s.pushHistory);
const { t } = useTranslation(); const { t } = useTranslation();
@ -91,6 +100,8 @@ export function Inpaint() {
return <Box> return <Box>
<Stack spacing={2}> <Stack spacing={2}>
<Profiles params={inpaint} setParams={setInpaint} highres={highres} setHighres={setHighres} upscale={upscale} setUpscale={setUpscale} />
<ModelControl model={model} setModel={setModel} />
{renderBanner()} {renderBanner()}
<ImageInput <ImageInput
filter={IMAGE_FILTER} filter={IMAGE_FILTER}
@ -115,6 +126,7 @@ export function Inpaint() {
}} }}
/> />
<MaskCanvas <MaskCanvas
brush={brush}
source={source} source={source}
mask={mask} mask={mask}
onSave={(file) => { onSave={(file) => {
@ -122,6 +134,7 @@ export function Inpaint() {
mask: file, mask: file,
}); });
}} }}
setBrush={setBrush}
/> />
<ImageControl <ImageControl
selector={(s) => s.inpaint} selector={(s) => s.inpaint}
@ -134,7 +147,7 @@ export function Inpaint() {
min={params.strength.min} min={params.strength.min}
max={params.strength.max} max={params.strength.max}
step={params.strength.step} step={params.strength.step}
value={strength} value={inpaint.strength}
onChange={(value) => { onChange={(value) => {
setInpaint({ setInpaint({
strength: value, strength: value,
@ -150,7 +163,7 @@ export function Inpaint() {
result: filters, result: filters,
selector: (f) => f.mask, selector: (f) => f.mask,
}} }}
value={filter} value={inpaint.filter}
onChange={(newFilter) => { onChange={(newFilter) => {
setInpaint({ setInpaint({
filter: newFilter, filter: newFilter,
@ -164,7 +177,7 @@ export function Inpaint() {
query={{ query={{
result: noises, result: noises,
}} }}
value={noise} value={inpaint.noise}
onChange={(newNoise) => { onChange={(newNoise) => {
setInpaint({ setInpaint({
noise: newNoise, noise: newNoise,
@ -176,7 +189,7 @@ export function Inpaint() {
<Select <Select
labelId={'outpaint-tiling'} labelId={'outpaint-tiling'}
label={t('parameter.tileOrder')} label={t('parameter.tileOrder')}
value={tileOrder} value={inpaint.tileOrder}
onChange={(e) => { onChange={(e) => {
setInpaint({ setInpaint({
tileOrder: e.target.value, tileOrder: e.target.value,
@ -194,7 +207,7 @@ export function Inpaint() {
sx={{ mx: 1 }} sx={{ mx: 1 }}
control={ control={
<input <input
defaultValue={fillColor} defaultValue={inpaint.fillColor}
name='fill-color' name='fill-color'
type='color' type='color'
onBlur={(event) => { onBlur={(event) => {
@ -208,8 +221,8 @@ export function Inpaint() {
</Stack> </Stack>
</Stack> </Stack>
<OutpaintControl /> <OutpaintControl />
<HighresControl /> <HighresControl highres={highres} setHighres={setHighres} />
<UpscaleControl /> <UpscaleControl upscale={upscale} setUpscale={setUpscale} />
<Button <Button
disabled={preventInpaint()} disabled={preventInpaint()}
variant='contained' variant='contained'

View File

@ -11,12 +11,13 @@ import { HighresControl } from '../control/HighresControl.js';
import { ImageControl } from '../control/ImageControl.js'; import { ImageControl } from '../control/ImageControl.js';
import { UpscaleControl } from '../control/UpscaleControl.js'; import { UpscaleControl } from '../control/UpscaleControl.js';
import { NumericField } from '../input/NumericField.js'; import { NumericField } from '../input/NumericField.js';
import { ModelControl } from '../control/ModelControl.js';
import { Profiles } from '../Profiles.js';
export function Txt2Img() { export function Txt2Img() {
const { params } = mustExist(useContext(ConfigContext)); const { params } = mustExist(useContext(ConfigContext));
async function generateImage() { async function generateImage() {
const { model, txt2img, upscale, highres } = state.getState();
const { image, retry } = await client.txt2img(model, txt2img, upscale, highres); const { image, retry } = await client.txt2img(model, txt2img, upscale, highres);
pushHistory(image, retry); pushHistory(image, retry);
@ -29,26 +30,36 @@ export function Txt2Img() {
}); });
const state = mustExist(useContext(StateContext)); const state = mustExist(useContext(StateContext));
const height = useStore(state, (s) => s.txt2img.height); const txt2img = useStore(state, (s) => s.txt2img);
const width = useStore(state, (s) => s.txt2img.width); const model = useStore(state, (s) => s.txt2imgModel);
const highres = useStore(state, (s) => s.txt2imgHighres);
const upscale = useStore(state, (s) => s.txt2imgUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setTxt2Img = useStore(state, (s) => s.setTxt2Img); const setParams = useStore(state, (s) => s.setTxt2Img);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setHighres = useStore(state, (s) => s.setTxt2ImgHighres);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setUpscale = useStore(state, (s) => s.setTxt2ImgUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setModel = useStore(state, (s) => s.setTxt2ImgModel);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const pushHistory = useStore(state, (s) => s.pushHistory); const pushHistory = useStore(state, (s) => s.pushHistory);
const { t } = useTranslation(); const { t } = useTranslation();
return <Box> return <Box>
<Stack spacing={2}> <Stack spacing={2}>
<ImageControl selector={(s) => s.txt2img} onChange={setTxt2Img} /> <Profiles params={txt2img} setParams={setParams} highres={highres} setHighres={setHighres} upscale={upscale} setUpscale={setUpscale} />
<ModelControl model={model} setModel={setModel} />
<ImageControl selector={(s) => s.txt2img} onChange={setParams} />
<Stack direction='row' spacing={4}> <Stack direction='row' spacing={4}>
<NumericField <NumericField
label={t('parameter.width')} label={t('parameter.width')}
min={params.width.min} min={params.width.min}
max={params.width.max} max={params.width.max}
step={params.width.step} step={params.width.step}
value={width} value={txt2img.width}
onChange={(value) => { onChange={(value) => {
setTxt2Img({ setParams({
width: value, width: value,
}); });
}} }}
@ -58,16 +69,16 @@ export function Txt2Img() {
min={params.height.min} min={params.height.min}
max={params.height.max} max={params.height.max}
step={params.height.step} step={params.height.step}
value={height} value={txt2img.height}
onChange={(value) => { onChange={(value) => {
setTxt2Img({ setParams({
height: value, height: value,
}); });
}} }}
/> />
</Stack> </Stack>
<HighresControl /> <HighresControl highres={highres} setHighres={setHighres} />
<UpscaleControl /> <UpscaleControl upscale={upscale} setUpscale={setUpscale} />
<Button <Button
variant='contained' variant='contained'
onClick={() => generate.mutate()} onClick={() => generate.mutate()}

View File

@ -1,25 +1,27 @@
import { doesExist, mustExist } from '@apextoaster/js-utils'; import { doesExist, mustExist } from '@apextoaster/js-utils';
import { Box, Button, Stack } from '@mui/material'; import { Box, Button, Stack } from '@mui/material';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import * as React from 'react'; import * as React from 'react';
import { useContext } from 'react'; import { useContext } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useStore } from 'zustand'; import { useStore } from 'zustand';
import { IMAGE_FILTER } from '../../config.js'; import { IMAGE_FILTER } from '../../config.js';
import { ClientContext, StateContext } from '../../state.js'; import { ClientContext, StateContext } from '../../state.js';
import { HighresControl } from '../control/HighresControl.js';
import { ModelControl } from '../control/ModelControl.js';
import { UpscaleControl } from '../control/UpscaleControl.js'; import { UpscaleControl } from '../control/UpscaleControl.js';
import { ImageInput } from '../input/ImageInput.js'; import { ImageInput } from '../input/ImageInput.js';
import { PromptInput } from '../input/PromptInput.js'; import { PromptInput } from '../input/PromptInput.js';
import { HighresControl } from '../control/HighresControl.js'; import { Profiles } from '../Profiles.js';
export function Upscale() { export function Upscale() {
async function uploadSource() { async function uploadSource() {
const { highres, model, upscale } = state.getState(); const { upscaleHighres, upscaleUpscale, upscaleModel, upscale } = state.getState();
const { image, retry } = await client.upscale(model, { const { image, retry } = await client.upscale(upscaleModel, {
...params, ...upscale,
source: mustExist(params.source), // TODO: show an error if this doesn't exist source: mustExist(upscale.source), // TODO: show an error if this doesn't exist
}, upscale, highres); }, upscaleUpscale, upscaleHighres);
pushHistory(image, retry); pushHistory(image, retry);
} }
@ -31,21 +33,32 @@ export function Upscale() {
}); });
const state = mustExist(useContext(StateContext)); const state = mustExist(useContext(StateContext));
const params = useStore(state, (s) => s.upscaleTab); const highres = useStore(state, (s) => s.upscaleHighres);
const model = useStore(state, (s) => s.upscaleModel);
const params = useStore(state, (s) => s.upscale);
const upscale = useStore(state, (s) => s.upscaleUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const setSource = useStore(state, (s) => s.setUpscaleTab); const setModel = useStore(state, (s) => s.setUpscalingModel);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setHighres = useStore(state, (s) => s.setUpscaleHighres);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setUpscale = useStore(state, (s) => s.setUpscaleUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method
const setParams = useStore(state, (s) => s.setUpscale);
// eslint-disable-next-line @typescript-eslint/unbound-method // eslint-disable-next-line @typescript-eslint/unbound-method
const pushHistory = useStore(state, (s) => s.pushHistory); const pushHistory = useStore(state, (s) => s.pushHistory);
const { t } = useTranslation(); const { t } = useTranslation();
return <Box> return <Box>
<Stack spacing={2}> <Stack spacing={2}>
<Profiles params={params} setParams={setParams} highres={highres} setHighres={setHighres} upscale={upscale} setUpscale={setUpscale} />
<ModelControl model={model} setModel={setModel} />
<ImageInput <ImageInput
filter={IMAGE_FILTER} filter={IMAGE_FILTER}
image={params.source} image={params.source}
label={t('input.image.source')} label={t('input.image.source')}
onChange={(file) => { onChange={(file) => {
setSource({ setParams({
source: file, source: file,
}); });
}} }}
@ -54,11 +67,11 @@ export function Upscale() {
prompt={params.prompt} prompt={params.prompt}
negativePrompt={params.negativePrompt} negativePrompt={params.negativePrompt}
onChange={(value) => { onChange={(value) => {
setSource(value); setParams(value);
}} }}
/> />
<HighresControl /> <HighresControl highres={highres} setHighres={setHighres} />
<UpscaleControl /> <UpscaleControl upscale={upscale} setUpscale={setUpscale} />
<Button <Button
disabled={doesExist(params.source) === false} disabled={doesExist(params.source) === false}
variant='contained' variant='contained'

View File

@ -47,35 +47,27 @@ export async function renderApp(config: Config, params: ServerParams, logger: Lo
// prep zustand with a slice for each tab, using local storage // prep zustand with a slice for each tab, using local storage
const { const {
createBrushSlice,
createDefaultSlice, createDefaultSlice,
createHistorySlice, createHistorySlice,
createImg2ImgSlice, createImg2ImgSlice,
createInpaintSlice, createInpaintSlice,
createModelSlice, createModelSlice,
createOutpaintSlice,
createTxt2ImgSlice, createTxt2ImgSlice,
createUpscaleSlice, createUpscaleSlice,
createHighresSlice,
createBlendSlice, createBlendSlice,
createResetSlice, createResetSlice,
createExtraSlice,
createProfileSlice, createProfileSlice,
} = createStateSlices(params); } = createStateSlices(params);
const state = createStore<OnnxState, [['zustand/persist', OnnxState]]>(persist((...slice) => ({ const state = createStore<OnnxState, [['zustand/persist', OnnxState]]>(persist((...slice) => ({
...createBrushSlice(...slice),
...createDefaultSlice(...slice), ...createDefaultSlice(...slice),
...createHistorySlice(...slice), ...createHistorySlice(...slice),
...createImg2ImgSlice(...slice), ...createImg2ImgSlice(...slice),
...createInpaintSlice(...slice), ...createInpaintSlice(...slice),
...createModelSlice(...slice), ...createModelSlice(...slice),
...createTxt2ImgSlice(...slice), ...createTxt2ImgSlice(...slice),
...createOutpaintSlice(...slice),
...createUpscaleSlice(...slice), ...createUpscaleSlice(...slice),
...createHighresSlice(...slice),
...createBlendSlice(...slice), ...createBlendSlice(...slice),
...createResetSlice(...slice), ...createResetSlice(...slice),
...createExtraSlice(...slice),
...createProfileSlice(...slice), ...createProfileSlice(...slice),
}), { }), {
name: STATE_KEY, name: STATE_KEY,
@ -91,8 +83,8 @@ export async function renderApp(config: Config, params: ServerParams, logger: Lo
mask: undefined, mask: undefined,
source: undefined, source: undefined,
}, },
upscaleTab: { upscale: {
...s.upscaleTab, ...s.upscale,
source: undefined, source: undefined,
}, },
blend: { blend: {

View File

@ -48,12 +48,6 @@ interface ProfileItem {
upscaleParams?: Maybe<UpscaleParams>; upscaleParams?: Maybe<UpscaleParams>;
} }
interface BrushSlice {
brush: BrushParams;
setBrush(brush: Partial<BrushParams>): void;
}
interface DefaultSlice { interface DefaultSlice {
defaults: TabState<BaseImgParams>; defaults: TabState<BaseImgParams>;
theme: Theme; theme: Theme;
@ -62,24 +56,6 @@ interface DefaultSlice {
setTheme(theme: Theme): void; setTheme(theme: Theme): void;
} }
interface ExtraSlice {
extras: ExtrasFile;
setExtras(extras: Partial<ExtrasFile>): void;
setCorrectionModel(model: CorrectionModel): void;
setDiffusionModel(model: DiffusionModel): void;
setExtraNetwork(model: ExtraNetwork): void;
setExtraSource(model: ExtraSource): void;
setUpscalingModel(model: UpscalingModel): void;
removeCorrectionModel(model: CorrectionModel): void;
removeDiffusionModel(model: DiffusionModel): void;
removeExtraNetwork(model: ExtraNetwork): void;
removeExtraSource(model: ExtraSource): void;
removeUpscalingModel(model: UpscalingModel): void;
}
interface HistorySlice { interface HistorySlice {
history: Array<HistoryItem>; history: Array<HistoryItem>;
limit: number; limit: number;
@ -91,60 +67,96 @@ interface HistorySlice {
} }
interface ModelSlice { interface ModelSlice {
model: ModelParams; extras: ExtrasFile;
setModel(model: Partial<ModelParams>): void; removeCorrectionModel(model: CorrectionModel): void;
removeDiffusionModel(model: DiffusionModel): void;
removeExtraNetwork(model: ExtraNetwork): void;
removeExtraSource(model: ExtraSource): void;
removeUpscalingModel(model: UpscalingModel): void;
setExtras(extras: Partial<ExtrasFile>): void;
setCorrectionModel(model: CorrectionModel): void;
setDiffusionModel(model: DiffusionModel): void;
setExtraNetwork(model: ExtraNetwork): void;
setExtraSource(model: ExtraSource): void;
setUpscalingModel(model: UpscalingModel): void;
} }
// #region tab slices // #region tab slices
interface Txt2ImgSlice { interface Txt2ImgSlice {
txt2img: TabState<Txt2ImgParams>; txt2img: TabState<Txt2ImgParams>;
txt2imgModel: ModelParams;
txt2imgHighres: HighresParams;
txt2imgUpscale: UpscaleParams;
resetTxt2Img(): void;
setTxt2Img(params: Partial<Txt2ImgParams>): void; setTxt2Img(params: Partial<Txt2ImgParams>): void;
resetTxt2Img(): void; setTxt2ImgModel(params: Partial<ModelParams>): void;
setTxt2ImgHighres(params: Partial<HighresParams>): void;
setTxt2ImgUpscale(params: Partial<UpscaleParams>): void;
} }
interface Img2ImgSlice { interface Img2ImgSlice {
img2img: TabState<Img2ImgParams>; img2img: TabState<Img2ImgParams>;
img2imgModel: ModelParams;
img2imgHighres: HighresParams;
img2imgUpscale: UpscaleParams;
resetImg2Img(): void;
setImg2Img(params: Partial<Img2ImgParams>): void; setImg2Img(params: Partial<Img2ImgParams>): void;
resetImg2Img(): void; setImg2ImgModel(params: Partial<ModelParams>): void;
setImg2ImgHighres(params: Partial<HighresParams>): void;
setImg2ImgUpscale(params: Partial<UpscaleParams>): void;
} }
interface InpaintSlice { interface InpaintSlice {
inpaint: TabState<InpaintParams>; inpaint: TabState<InpaintParams>;
inpaintBrush: BrushParams;
setInpaint(params: Partial<InpaintParams>): void; inpaintModel: ModelParams;
resetInpaint(): void; inpaintHighres: HighresParams;
} inpaintUpscale: UpscaleParams;
interface OutpaintSlice {
outpaint: OutpaintPixels; outpaint: OutpaintPixels;
resetInpaint(): void;
setInpaint(params: Partial<InpaintParams>): void;
setInpaintBrush(brush: Partial<BrushParams>): void;
setInpaintModel(params: Partial<ModelParams>): void;
setInpaintHighres(params: Partial<HighresParams>): void;
setInpaintUpscale(params: Partial<UpscaleParams>): void;
setOutpaint(pixels: Partial<OutpaintPixels>): void; setOutpaint(pixels: Partial<OutpaintPixels>): void;
} }
interface HighresSlice {
highres: HighresParams;
setHighres(params: Partial<HighresParams>): void;
resetHighres(): void;
}
interface UpscaleSlice { interface UpscaleSlice {
upscale: UpscaleParams; upscale: TabState<UpscaleReqParams>;
upscaleTab: TabState<UpscaleReqParams>; upscaleHighres: HighresParams;
upscaleModel: ModelParams;
upscaleUpscale: UpscaleParams;
setUpscale(upscale: Partial<UpscaleParams>): void; resetUpscale(): void;
setUpscaleTab(params: Partial<UpscaleReqParams>): void;
resetUpscaleTab(): void; setUpscale(params: Partial<UpscaleReqParams>): void;
setUpscaleHighres(params: Partial<HighresParams>): void;
setUpscaleModel(params: Partial<ModelParams>): void;
setUpscaleUpscale(params: Partial<UpscaleParams>): void;
} }
interface BlendSlice { interface BlendSlice {
blend: TabState<BlendParams>; blend: TabState<BlendParams>;
blendBrush: BrushParams;
blendModel: ModelParams;
blendUpscale: UpscaleParams;
resetBlend(): void;
setBlend(blend: Partial<BlendParams>): void; setBlend(blend: Partial<BlendParams>): void;
resetBlend(): void; setBlendBrush(brush: Partial<BrushParams>): void;
setBlendModel(model: Partial<ModelParams>): void;
setBlendUpscale(params: Partial<UpscaleParams>): void;
} }
interface ResetSlice { interface ResetSlice {
@ -154,8 +166,9 @@ interface ResetSlice {
interface ProfileSlice { interface ProfileSlice {
profiles: Array<ProfileItem>; profiles: Array<ProfileItem>;
saveProfile(profile: ProfileItem): void;
removeProfile(profileName: string): void; removeProfile(profileName: string): void;
saveProfile(profile: ProfileItem): void;
} }
// #endregion // #endregion
@ -163,19 +176,16 @@ interface ProfileSlice {
* Full merged state including all slices. * Full merged state including all slices.
*/ */
export type OnnxState export type OnnxState
= BrushSlice = DefaultSlice
& DefaultSlice
& HistorySlice & HistorySlice
& Img2ImgSlice & Img2ImgSlice
& InpaintSlice & InpaintSlice
& ModelSlice & ModelSlice
& OutpaintSlice
& Txt2ImgSlice & Txt2ImgSlice
& HighresSlice
& UpscaleSlice & UpscaleSlice
& BlendSlice & BlendSlice
& ResetSlice & ResetSlice
& ExtraSlice & ModelSlice
& ProfileSlice; & ProfileSlice;
/** /**
@ -268,14 +278,49 @@ export function baseParamsFromServer(defaults: ServerParams): Required<BaseImgPa
* else should be initialized from the default value in the base parameters. * else should be initialized from the default value in the base parameters.
*/ */
export function createStateSlices(server: ServerParams) { export function createStateSlices(server: ServerParams) {
const base = baseParamsFromServer(server); const defaultParams = baseParamsFromServer(server);
const defaultHighres: HighresParams = {
enabled: false,
highresIterations: server.highresIterations.default,
highresMethod: '',
highresSteps: server.highresSteps.default,
highresScale: server.highresScale.default,
highresStrength: server.highresStrength.default,
};
const defaultModel: ModelParams = {
control: server.control.default,
correction: server.correction.default,
model: server.model.default,
pipeline: server.pipeline.default,
platform: server.platform.default,
upscaling: server.upscaling.default,
};
const defaultUpscale: UpscaleParams = {
denoise: server.denoise.default,
enabled: false,
faces: false,
faceOutscale: server.faceOutscale.default,
faceStrength: server.faceStrength.default,
outscale: server.outscale.default,
scale: server.scale.default,
upscaleOrder: server.upscaleOrder.default,
};
const createTxt2ImgSlice: Slice<Txt2ImgSlice> = (set) => ({ const createTxt2ImgSlice: Slice<Txt2ImgSlice> = (set) => ({
txt2img: { txt2img: {
...base, ...defaultParams,
width: server.width.default, width: server.width.default,
height: server.height.default, height: server.height.default,
}, },
txt2imgHighres: {
...defaultHighres,
},
txt2imgModel: {
...defaultModel,
},
txt2imgUpscale: {
...defaultUpscale,
},
setTxt2Img(params) { setTxt2Img(params) {
set((prev) => ({ set((prev) => ({
txt2img: { txt2img: {
@ -284,10 +329,34 @@ export function createStateSlices(server: ServerParams) {
}, },
})); }));
}, },
setTxt2ImgHighres(params) {
set((prev) => ({
txt2imgHighres: {
...prev.txt2imgHighres,
...params,
},
}));
},
setTxt2ImgModel(params) {
set((prev) => ({
txt2imgModel: {
...prev.txt2imgModel,
...params,
},
}));
},
setTxt2ImgUpscale(params) {
set((prev) => ({
txt2imgUpscale: {
...prev.txt2imgUpscale,
...params,
},
}));
},
resetTxt2Img() { resetTxt2Img() {
set({ set({
txt2img: { txt2img: {
...base, ...defaultParams,
width: server.width.default, width: server.width.default,
height: server.height.default, height: server.height.default,
}, },
@ -297,12 +366,32 @@ export function createStateSlices(server: ServerParams) {
const createImg2ImgSlice: Slice<Img2ImgSlice> = (set) => ({ const createImg2ImgSlice: Slice<Img2ImgSlice> = (set) => ({
img2img: { img2img: {
...base, ...defaultParams,
loopback: server.loopback.default, loopback: server.loopback.default,
source: null, source: null,
sourceFilter: '', sourceFilter: '',
strength: server.strength.default, strength: server.strength.default,
}, },
img2imgHighres: {
...defaultHighres,
},
img2imgModel: {
...defaultModel,
},
img2imgUpscale: {
...defaultUpscale,
},
resetImg2Img() {
set({
img2img: {
...defaultParams,
loopback: server.loopback.default,
source: null,
sourceFilter: '',
strength: server.strength.default,
},
});
},
setImg2Img(params) { setImg2Img(params) {
set((prev) => ({ set((prev) => ({
img2img: { img2img: {
@ -311,22 +400,35 @@ export function createStateSlices(server: ServerParams) {
}, },
})); }));
}, },
resetImg2Img() { setImg2ImgHighres(params) {
set({ set((prev) => ({
img2img: { img2imgHighres: {
...base, ...prev.img2imgHighres,
loopback: server.loopback.default, ...params,
source: null,
sourceFilter: '',
strength: server.strength.default,
}, },
}); }));
},
setImg2ImgModel(params) {
set((prev) => ({
img2imgModel: {
...prev.img2imgModel,
...params,
},
}));
},
setImg2ImgUpscale(params) {
set((prev) => ({
img2imgUpscale: {
...prev.img2imgUpscale,
...params,
},
}));
}, },
}); });
const createInpaintSlice: Slice<InpaintSlice> = (set) => ({ const createInpaintSlice: Slice<InpaintSlice> = (set) => ({
inpaint: { inpaint: {
...base, ...defaultParams,
fillColor: server.fillColor.default, fillColor: server.fillColor.default,
filter: server.filter.default, filter: server.filter.default,
mask: null, mask: null,
@ -335,18 +437,29 @@ export function createStateSlices(server: ServerParams) {
strength: server.strength.default, strength: server.strength.default,
tileOrder: server.tileOrder.default, tileOrder: server.tileOrder.default,
}, },
setInpaint(params) { inpaintBrush: {
set((prev) => ({ ...DEFAULT_BRUSH,
inpaint: { },
...prev.inpaint, inpaintHighres: {
...params, ...defaultHighres,
}, },
})); inpaintModel: {
...defaultModel,
},
inpaintUpscale: {
...defaultUpscale,
},
outpaint: {
enabled: false,
left: server.left.default,
right: server.right.default,
top: server.top.default,
bottom: server.bottom.default,
}, },
resetInpaint() { resetInpaint() {
set({ set({
inpaint: { inpaint: {
...base, ...defaultParams,
fillColor: server.fillColor.default, fillColor: server.fillColor.default,
filter: server.filter.default, filter: server.filter.default,
mask: null, mask: null,
@ -357,6 +470,54 @@ export function createStateSlices(server: ServerParams) {
}, },
}); });
}, },
setInpaint(params) {
set((prev) => ({
inpaint: {
...prev.inpaint,
...params,
},
}));
},
setInpaintBrush(brush) {
set((prev) => ({
inpaintBrush: {
...prev.inpaintBrush,
...brush,
},
}));
},
setInpaintHighres(params) {
set((prev) => ({
inpaintHighres: {
...prev.inpaintHighres,
...params,
},
}));
},
setInpaintModel(params) {
set((prev) => ({
inpaintModel: {
...prev.inpaintModel,
...params,
},
}));
},
setInpaintUpscale(params) {
set((prev) => ({
inpaintUpscale: {
...prev.inpaintUpscale,
...params,
},
}));
},
setOutpaint(pixels) {
set((prev) => ({
outpaint: {
...prev.outpaint,
...pixels,
}
}));
},
}); });
const createHistorySlice: Slice<HistorySlice> = (set) => ({ const createHistorySlice: Slice<HistorySlice> = (set) => ({
@ -405,109 +566,59 @@ export function createStateSlices(server: ServerParams) {
}, },
}); });
const createOutpaintSlice: Slice<OutpaintSlice> = (set) => ({
outpaint: {
enabled: false,
left: server.left.default,
right: server.right.default,
top: server.top.default,
bottom: server.bottom.default,
},
setOutpaint(pixels) {
set((prev) => ({
outpaint: {
...prev.outpaint,
...pixels,
}
}));
},
});
const createBrushSlice: Slice<BrushSlice> = (set) => ({
brush: {
...DEFAULT_BRUSH,
},
setBrush(brush) {
set((prev) => ({
brush: {
...prev.brush,
...brush,
},
}));
},
});
const createUpscaleSlice: Slice<UpscaleSlice> = (set) => ({ const createUpscaleSlice: Slice<UpscaleSlice> = (set) => ({
upscale: { upscale: {
denoise: server.denoise.default, ...defaultParams,
enabled: false,
faces: false,
faceOutscale: server.faceOutscale.default,
faceStrength: server.faceStrength.default,
outscale: server.outscale.default,
scale: server.scale.default,
upscaleOrder: server.upscaleOrder.default,
},
upscaleTab: {
negativePrompt: server.negativePrompt.default,
prompt: server.prompt.default,
source: null, source: null,
}, },
setUpscale(upscale) { upscaleHighres: {
set((prev) => ({ ...defaultHighres,
upscale: {
...prev.upscale,
...upscale,
},
}));
}, },
setUpscaleTab(source) { upscaleModel: {
set((prev) => ({ ...defaultModel,
upscaleTab: {
...prev.upscaleTab,
...source,
},
}));
}, },
resetUpscaleTab() { upscaleUpscale: {
...defaultUpscale,
},
resetUpscale() {
set({ set({
upscaleTab: { upscale: {
negativePrompt: server.negativePrompt.default, ...defaultParams,
prompt: server.prompt.default,
source: null, source: null,
}, },
}); });
}, },
}); setUpscale(source) {
const createHighresSlice: Slice<HighresSlice> = (set) => ({
highres: {
enabled: false,
highresIterations: server.highresIterations.default,
highresMethod: '',
highresSteps: server.highresSteps.default,
highresScale: server.highresScale.default,
highresStrength: server.highresStrength.default,
},
setHighres(params) {
set((prev) => ({ set((prev) => ({
highres: { upscale: {
...prev.highres, ...prev.upscale,
...source,
},
}));
},
setUpscaleHighres(params) {
set((prev) => ({
upscaleHighres: {
...prev.upscaleHighres,
...params, ...params,
}, },
})); }));
}, },
resetHighres() { setUpscaleModel(params) {
set({ set((prev) => ({
highres: { upscaleModel: {
enabled: false, ...prev.upscaleModel,
highresIterations: server.highresIterations.default, ...defaultModel,
highresMethod: '',
highresSteps: server.highresSteps.default,
highresScale: server.highresScale.default,
highresStrength: server.highresStrength.default,
}, },
}); }));
},
setUpscaleUpscale(params) {
set((prev) => ({
upscaleUpscale: {
...prev.upscaleUpscale,
...params,
},
}));
}, },
}); });
@ -516,13 +627,14 @@ export function createStateSlices(server: ServerParams) {
mask: null, mask: null,
sources: [], sources: [],
}, },
setBlend(blend) { blendBrush: {
set((prev) => ({ ...DEFAULT_BRUSH,
blend: { },
...prev.blend, blendModel: {
...blend, ...defaultModel,
}, },
})); blendUpscale: {
...defaultUpscale,
}, },
resetBlend() { resetBlend() {
set({ set({
@ -532,11 +644,43 @@ export function createStateSlices(server: ServerParams) {
}, },
}); });
}, },
setBlend(blend) {
set((prev) => ({
blend: {
...prev.blend,
...blend,
},
}));
},
setBlendBrush(brush) {
set((prev) => ({
blendBrush: {
...prev.blendBrush,
...brush,
},
}));
},
setBlendModel(model) {
set((prev) => ({
blendModel: {
...prev.blendModel,
...model,
},
}));
},
setBlendUpscale(params) {
set((prev) => ({
blendUpscale: {
...prev.blendUpscale,
...params,
},
}));
},
}); });
const createDefaultSlice: Slice<DefaultSlice> = (set) => ({ const createDefaultSlice: Slice<DefaultSlice> = (set) => ({
defaults: { defaults: {
...base, ...defaultParams,
}, },
theme: '', theme: '',
setDefaults(params) { setDefaults(params) {
@ -554,25 +698,6 @@ export function createStateSlices(server: ServerParams) {
} }
}); });
const createModelSlice: Slice<ModelSlice> = (set) => ({
model: {
control: server.control.default,
correction: server.correction.default,
model: server.model.default,
pipeline: server.pipeline.default,
platform: server.platform.default,
upscaling: server.upscaling.default,
},
setModel(params) {
set((prev) => ({
model: {
...prev.model,
...params,
}
}));
},
});
const createResetSlice: Slice<ResetSlice> = (set) => ({ const createResetSlice: Slice<ResetSlice> = (set) => ({
resetAll() { resetAll() {
set((prev) => { set((prev) => {
@ -580,7 +705,7 @@ export function createStateSlices(server: ServerParams) {
next.resetImg2Img(); next.resetImg2Img();
next.resetInpaint(); next.resetInpaint();
next.resetTxt2Img(); next.resetTxt2Img();
next.resetUpscaleTab(); next.resetUpscale();
next.resetBlend(); next.resetBlend();
return next; return next;
}); });
@ -620,7 +745,7 @@ export function createStateSlices(server: ServerParams) {
}); });
// eslint-disable-next-line sonarjs/cognitive-complexity // eslint-disable-next-line sonarjs/cognitive-complexity
const createExtraSlice: Slice<ExtraSlice> = (set) => ({ const createModelSlice: Slice<ModelSlice> = (set) => ({
extras: { extras: {
correction: [], correction: [],
diffusion: [], diffusion: [],
@ -799,19 +924,15 @@ export function createStateSlices(server: ServerParams) {
}); });
return { return {
createBrushSlice,
createDefaultSlice, createDefaultSlice,
createHistorySlice, createHistorySlice,
createImg2ImgSlice, createImg2ImgSlice,
createInpaintSlice, createInpaintSlice,
createModelSlice,
createOutpaintSlice,
createTxt2ImgSlice, createTxt2ImgSlice,
createUpscaleSlice, createUpscaleSlice,
createHighresSlice,
createBlendSlice, createBlendSlice,
createResetSlice, createResetSlice,
createExtraSlice, createModelSlice,
createProfileSlice, createProfileSlice,
}; };
} }