1
0
Fork 0

feat(gui): add menus to add LoRA and Textual Inversion tokens

This commit is contained in:
Sean Sube 2023-03-18 21:48:06 -05:00
parent a9e55ff9f2
commit b195b59301
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
2 changed files with 141 additions and 8 deletions

View File

@ -9,6 +9,7 @@ import { useStore } from 'zustand';
import { STALE_TIME } from '../../config.js';
import { ClientContext, StateContext } from '../../state.js';
import { QueryList } from '../input/QueryList.js';
import { QueryMenu } from '../input/QueryMenu.js';
export function ModelControl() {
const client = mustExist(useContext(ClientContext));
@ -97,7 +98,7 @@ export function ModelControl() {
}}
/>}
/>
<QueryList
<QueryMenu
id='inversion'
labelKey='model.inversion'
name={t('modelType.inversion')}
@ -105,12 +106,16 @@ export function ModelControl() {
result: models,
selector: (result) => result.networks.filter((network) => network.type === 'inversion').map((network) => network.name),
}}
value={params.correction}
onChange={(correction) => {
// noop
onSelect={(name) => {
const current = state.getState();
const { prompt } = current.txt2img;
current.setTxt2Img({
prompt: `<inversion:${name}:1.0> ${prompt}`,
});
}}
/>
<QueryList
<QueryMenu
id='lora'
labelKey='model.lora'
name={t('modelType.lora')}
@ -118,9 +123,13 @@ export function ModelControl() {
result: models,
selector: (result) => result.networks.filter((network) => network.type === 'lora').map((network) => network.name),
}}
value={params.correction}
onChange={(correction) => {
// noop
onSelect={(name) => {
const current = state.getState();
const { prompt } = current.txt2img;
current.setTxt2Img({
prompt: `<lora:${name}:1.0> ${prompt}`,
});
}}
/>
</Stack>;

View File

@ -0,0 +1,124 @@
import { doesExist, Maybe, mustDefault, mustExist } from '@apextoaster/js-utils';
import { KeyboardArrowDown } from '@mui/icons-material';
import { Alert, Box, Button, FormControl, FormLabel, LinearProgress, Menu, MenuItem, Typography } from '@mui/material';
import * as React from 'react';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { UseQueryResult } from 'react-query';
export interface QueryMenuComplete {
result: UseQueryResult<Array<string>>;
}
export interface QueryMenuFilter<T> {
result: UseQueryResult<T>;
selector: (result: T) => Array<string>;
}
export interface QueryMenuProps<T> {
id: string;
labelKey: string;
name: string;
query: QueryMenuComplete | QueryMenuFilter<T>;
showEmpty?: boolean;
onSelect?: (value: string) => void;
}
export function hasFilter<T>(query: QueryMenuComplete | QueryMenuFilter<T>): query is QueryMenuFilter<T> {
return Reflect.has(query, 'selector');
}
export function filterQuery<T>(query: QueryMenuComplete | QueryMenuFilter<T>, showEmpty: boolean): Array<string> {
if (hasFilter(query)) {
const data = mustExist(query.result.data);
const selected = (query as QueryMenuFilter<unknown>).selector(data);
if (showEmpty) {
return ['', ...selected];
}
return selected;
} else {
const data = Array.from(mustExist(query.result.data));
if (showEmpty) {
return ['', ...data];
}
return data;
}
}
export function QueryMenu<T>(props: QueryMenuProps<T>) {
const { id, labelKey, name, query, showEmpty = false } = props;
const { result } = query;
const labelID = `query-menu-${props.id}-labels`;
const { t } = useTranslation();
const [anchor, setAnchor] = React.useState<Maybe<HTMLElement>>(undefined);
function closeMenu() {
setAnchor(undefined);
}
function openMenu(event: React.MouseEvent<HTMLButtonElement>) {
setAnchor(event.currentTarget);
}
function selectItem(value: string) {
closeMenu();
if (doesExist(props.onSelect)) {
props.onSelect(value);
}
}
function getLabel(key: string) {
return mustDefault(t(`${labelKey}.${key}`), key);
}
if (result.status === 'error') {
if (result.error instanceof Error) {
return <Alert severity='error'>{t('input.list.error.specific', {
message: result.error.message,
})}</Alert>;
} else {
return <Alert severity='error'>{t('input.list.error.unknown')}</Alert>;
}
}
if (result.status === 'loading') {
return <FormControl>
<FormLabel id={labelID}>{props.name}</FormLabel>
<LinearProgress />
</FormControl>;
}
if (result.status === 'idle') {
return <Typography>{t('input.list.idle')}</Typography>;
}
// else: success
const data = filterQuery(query, showEmpty);
return <Box>
<Button
id={`${id}-button`}
onClick={openMenu}
endIcon={<KeyboardArrowDown />}
variant='outlined'
>
{name}
</Button>
<Menu
id={`${id}-menu`}
anchorEl={anchor}
open={doesExist(anchor)}
onClose={closeMenu}
MenuListProps={{
'aria-labelledby': `${id}-button`,
}}
>
{data.map((it, idx) => <MenuItem key={idx} onClick={() => selectItem(it)}>{getLabel(it)}</MenuItem>)}
</Menu>
</Box>;
}