add scheduler dropdown, wrap numeric input
This commit is contained in:
parent
6b4545b3bf
commit
75dde1dd75
|
@ -4,8 +4,15 @@ export interface Txt2ImgParams {
|
||||||
prompt: string;
|
prompt: string;
|
||||||
cfg: number;
|
cfg: number;
|
||||||
steps: number;
|
steps: number;
|
||||||
width: number;
|
width?: number;
|
||||||
height: number;
|
height?: number;
|
||||||
|
seed?: string;
|
||||||
|
scheduler?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiResponse {
|
||||||
|
params: Txt2ImgParams;
|
||||||
|
path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiClient {
|
export interface ApiClient {
|
||||||
|
@ -34,8 +41,23 @@ export function makeClient(root: string, f = fetch): ApiClient {
|
||||||
const url = new URL('/txt2img', root);
|
const url = new URL('/txt2img', root);
|
||||||
url.searchParams.append('cfg', params.cfg.toFixed(0));
|
url.searchParams.append('cfg', params.cfg.toFixed(0));
|
||||||
url.searchParams.append('steps', params.steps.toFixed(0));
|
url.searchParams.append('steps', params.steps.toFixed(0));
|
||||||
|
|
||||||
|
if (doesExist(params.width)) {
|
||||||
url.searchParams.append('width', params.width.toFixed(0));
|
url.searchParams.append('width', params.width.toFixed(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doesExist(params.height)) {
|
||||||
url.searchParams.append('height', params.height.toFixed(0));
|
url.searchParams.append('height', params.height.toFixed(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doesExist(params.seed)) {
|
||||||
|
url.searchParams.append('seed', params.seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doesExist(params.scheduler)) {
|
||||||
|
url.searchParams.append('scheduler', params.scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
url.searchParams.append('prompt', params.prompt);
|
url.searchParams.append('prompt', params.prompt);
|
||||||
|
|
||||||
pending = f(url).then((res) => {
|
pending = f(url).then((res) => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { doesExist } from '@apextoaster/js-utils';
|
import { doesExist } from '@apextoaster/js-utils';
|
||||||
import { Container, Stack, TextField } from '@mui/material';
|
import { Stack, TextField } from '@mui/material';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { NumericField } from './NumericField';
|
||||||
|
|
||||||
export interface ImageParams {
|
export interface ImageParams {
|
||||||
cfg: number;
|
cfg: number;
|
||||||
|
@ -19,64 +20,64 @@ export function ImageControl(props: ImageControlProps) {
|
||||||
|
|
||||||
return <Stack spacing={2}>
|
return <Stack spacing={2}>
|
||||||
<Stack direction="row" spacing={4}>
|
<Stack direction="row" spacing={4}>
|
||||||
<TextField
|
<NumericField
|
||||||
label="CFG"
|
label="CFG"
|
||||||
variant="outlined"
|
min={0}
|
||||||
type="number"
|
max={30}
|
||||||
inputProps={{ min: 0, max: 30, step: 1 }}
|
step={1}
|
||||||
value={params.cfg}
|
value={params.cfg}
|
||||||
onChange={(event) => {
|
onChange={(cfg) => {
|
||||||
if (doesExist(props.onChange)) {
|
if (doesExist(props.onChange)) {
|
||||||
props.onChange({
|
props.onChange({
|
||||||
...params,
|
...params,
|
||||||
cfg: parseInt(event.target.value, 10),
|
cfg,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<NumericField
|
||||||
label="Steps"
|
label="Steps"
|
||||||
variant="outlined"
|
min={1}
|
||||||
type="number"
|
max={150}
|
||||||
inputProps={{ min: 1, max: 150, step: 1 }}
|
step={1}
|
||||||
value={params.steps}
|
value={params.steps}
|
||||||
onChange={(event) => {
|
onChange={(steps) => {
|
||||||
if (doesExist(props.onChange)) {
|
if (doesExist(props.onChange)) {
|
||||||
props.onChange({
|
props.onChange({
|
||||||
...params,
|
...params,
|
||||||
steps: parseInt(event.target.value, 10),
|
steps,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack direction="row" spacing={4}>
|
<Stack direction="row" spacing={4}>
|
||||||
<TextField
|
<NumericField
|
||||||
label="Width"
|
label="Width"
|
||||||
variant="outlined"
|
min={1}
|
||||||
type="number"
|
max={512}
|
||||||
inputProps={{ min: 1, max: 512, step: 16 }}
|
step={8}
|
||||||
value={params.width}
|
value={params.width}
|
||||||
onChange={(event) => {
|
onChange={(width) => {
|
||||||
if (doesExist(props.onChange)) {
|
if (doesExist(props.onChange)) {
|
||||||
props.onChange({
|
props.onChange({
|
||||||
...params,
|
...params,
|
||||||
width: parseInt(event.target.value, 10),
|
width,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<NumericField
|
||||||
label="Height"
|
label="Height"
|
||||||
variant="outlined"
|
min={1}
|
||||||
type="number"
|
max={512}
|
||||||
inputProps={{ min: 1, max: 512, step: 16 }}
|
step={8}
|
||||||
value={params.height}
|
value={params.height}
|
||||||
onChange={(event) => {
|
onChange={(height) => {
|
||||||
if (doesExist(props.onChange)) {
|
if (doesExist(props.onChange)) {
|
||||||
props.onChange({
|
props.onChange({
|
||||||
...params,
|
...params,
|
||||||
height: parseInt(event.target.value, 10),
|
height,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { doesExist } from '@apextoaster/js-utils';
|
||||||
|
import { TextField } from '@mui/material';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export interface ImageControlProps {
|
||||||
|
label: string;
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
step: number;
|
||||||
|
value: number;
|
||||||
|
|
||||||
|
onChange?: (value: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NumericField(props: ImageControlProps) {
|
||||||
|
const { label, min, max, step, value } = props;
|
||||||
|
return <TextField
|
||||||
|
label={label}
|
||||||
|
variant="outlined"
|
||||||
|
type="number"
|
||||||
|
inputProps={{ min, max, step }}
|
||||||
|
value={value}
|
||||||
|
onChange={(event) => {
|
||||||
|
if (doesExist(props.onChange)) {
|
||||||
|
props.onChange(parseInt(event.target.value, 10));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { Box, Button, Stack, TextField } from '@mui/material';
|
import { Box, Button, MenuItem, Select, Stack, TextField } from '@mui/material';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { ApiClient } from '../api/client.js';
|
import { ApiClient } from '../api/client.js';
|
||||||
|
@ -20,10 +20,11 @@ export function Txt2Img(props: Txt2ImgProps) {
|
||||||
steps: 25,
|
steps: 25,
|
||||||
width: 512,
|
width: 512,
|
||||||
height: 512,
|
height: 512,
|
||||||
})
|
});
|
||||||
|
const [scheduler, setScheduler] = useState('euler-a');
|
||||||
|
|
||||||
async function getImage() {
|
async function getImage() {
|
||||||
const image = await client.txt2img({ ...params, prompt });
|
const image = await client.txt2img({ ...params, prompt, scheduler });
|
||||||
console.log(prompt, image);
|
console.log(prompt, image);
|
||||||
setImage(image);
|
setImage(image);
|
||||||
}
|
}
|
||||||
|
@ -41,6 +42,21 @@ export function Txt2Img(props: Txt2ImgProps) {
|
||||||
<Box>
|
<Box>
|
||||||
txt2img mode
|
txt2img mode
|
||||||
</Box>
|
</Box>
|
||||||
|
<Select
|
||||||
|
value={scheduler}
|
||||||
|
label="Scheduler"
|
||||||
|
onChange={(event) => {
|
||||||
|
setScheduler(event.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value='ddim'>DDIM</MenuItem>
|
||||||
|
<MenuItem value='ddpm'>DDPM</MenuItem>
|
||||||
|
<MenuItem value='pndm'>PNDM</MenuItem>
|
||||||
|
<MenuItem value='lms-discrete'>LMS</MenuItem>
|
||||||
|
<MenuItem value='euler'>Euler</MenuItem>
|
||||||
|
<MenuItem value='euler-a'>Euler A</MenuItem>
|
||||||
|
<MenuItem value='dpm-multi'>DPM</MenuItem>
|
||||||
|
</Select>
|
||||||
<ImageControl params={params} onChange={(params) => {
|
<ImageControl params={params} onChange={(params) => {
|
||||||
setParams(params);
|
setParams(params);
|
||||||
}} />
|
}} />
|
||||||
|
|
Loading…
Reference in New Issue