fix(gui): dedupe query lists into a component
This commit is contained in:
parent
cab13f665a
commit
1c9eed3a90
|
@ -1,10 +1,10 @@
|
||||||
import { mustExist } from '@apextoaster/js-utils';
|
|
||||||
import { TabContext, TabList, TabPanel } from '@mui/lab';
|
import { TabContext, TabList, TabPanel } from '@mui/lab';
|
||||||
import { Box, Container, MenuItem, Select, Tab, Typography } from '@mui/material';
|
import { Box, Container, Tab, Typography } from '@mui/material';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
import { ApiClient } from '../api/client.js';
|
import { ApiClient } from '../api/client.js';
|
||||||
|
import { QueryList } from './QueryList.js';
|
||||||
import { STALE_TIME, Txt2Img } from './Txt2Img.js';
|
import { STALE_TIME, Txt2Img } from './Txt2Img.js';
|
||||||
|
|
||||||
const { useState } = React;
|
const { useState } = React;
|
||||||
|
@ -13,6 +13,10 @@ export interface OnnxWebProps {
|
||||||
client: ApiClient;
|
client: ApiClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MODEL_LABELS = {
|
||||||
|
'stable-diffusion-onnx-v1-5': 'Stable Diffusion v1.5',
|
||||||
|
};
|
||||||
|
|
||||||
export function OnnxWeb(props: OnnxWebProps) {
|
export function OnnxWeb(props: OnnxWebProps) {
|
||||||
const [tab, setTab] = useState('1');
|
const [tab, setTab] = useState('1');
|
||||||
const [model, setModel] = useState('stable-diffusion-onnx-v1-5');
|
const [model, setModel] = useState('stable-diffusion-onnx-v1-5');
|
||||||
|
@ -21,19 +25,6 @@ export function OnnxWeb(props: OnnxWebProps) {
|
||||||
staleTime: STALE_TIME,
|
staleTime: STALE_TIME,
|
||||||
});
|
});
|
||||||
|
|
||||||
function renderModels() {
|
|
||||||
switch (models.status) {
|
|
||||||
case 'error':
|
|
||||||
return <MenuItem value='error'>Error</MenuItem>;
|
|
||||||
case 'loading':
|
|
||||||
return <MenuItem value='loading'>Loading</MenuItem>;
|
|
||||||
case 'success':
|
|
||||||
return mustExist(models.data).map((name) => <MenuItem key={name} value={name}>{name}</MenuItem>);
|
|
||||||
default:
|
|
||||||
return <MenuItem value='error'>Unknown Error</MenuItem>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Container>
|
<Container>
|
||||||
|
@ -43,11 +34,9 @@ export function OnnxWeb(props: OnnxWebProps) {
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ my: 4 }}>
|
<Box sx={{ my: 4 }}>
|
||||||
<Select value={model} onChange={(e) => {
|
<QueryList result={models} labels={MODEL_LABELS} value={model} onChange={(value) => {
|
||||||
setModel(e.target.value);
|
setModel(value);
|
||||||
}}>
|
}} />
|
||||||
{renderModels()}
|
|
||||||
</Select>
|
|
||||||
</Box>
|
</Box>
|
||||||
<TabContext value={tab}>
|
<TabContext value={tab}>
|
||||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { doesExist, mustDefault, mustExist } from '@apextoaster/js-utils';
|
||||||
|
import { MenuItem, Select } from '@mui/material';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { UseQueryResult } from 'react-query';
|
||||||
|
|
||||||
|
export interface QueryListProps {
|
||||||
|
labels: Record<string, string>;
|
||||||
|
result: UseQueryResult<Array<string>>;
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
onChange?: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function QueryList(props: QueryListProps) {
|
||||||
|
const { labels, result, value } = props;
|
||||||
|
|
||||||
|
if (result.status === 'error') {
|
||||||
|
if (result.error instanceof Error) {
|
||||||
|
return <div>Error: {result.error.message}</div>;
|
||||||
|
} else {
|
||||||
|
return <div>Unknown Error</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status === 'loading') {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status === 'idle') {
|
||||||
|
return <div>Idle?</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else: success
|
||||||
|
const data = mustExist(result.data);
|
||||||
|
return <Select value={value} onChange={(e) => {
|
||||||
|
if (doesExist(props.onChange)) {
|
||||||
|
props.onChange(e.target.value);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
{data.map((name) => <MenuItem key={name} value={name}>{mustDefault(labels[name], name)}</MenuItem>)}
|
||||||
|
</Select>;
|
||||||
|
}
|
|
@ -1,22 +1,22 @@
|
||||||
import { mustExist } from '@apextoaster/js-utils';
|
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 { useMutation, useQuery } from 'react-query';
|
import { useMutation, useQuery } from 'react-query';
|
||||||
|
|
||||||
import { ApiClient } from '../api/client.js';
|
import { ApiClient } from '../api/client.js';
|
||||||
import { ImageControl, ImageParams } from './ImageControl.js';
|
import { ImageControl, ImageParams } from './ImageControl.js';
|
||||||
|
import { QueryList } from './QueryList.js';
|
||||||
|
|
||||||
const { useState } = React;
|
const { useState } = React;
|
||||||
|
|
||||||
export const STALE_TIME = 3_000;
|
export const STALE_TIME = 3_000;
|
||||||
|
|
||||||
// TODO: set up i18next
|
// TODO: set up i18next
|
||||||
const PLATFORM_NAMES: Record<string, string> = {
|
const PLATFORM_LABELS: Record<string, string> = {
|
||||||
amd: 'AMD GPU',
|
amd: 'AMD GPU',
|
||||||
cpu: 'CPU',
|
cpu: 'CPU',
|
||||||
};
|
};
|
||||||
|
|
||||||
const SCHEDULER_NAMES: Record<string, string> = {
|
const SCHEDULER_LABELS: Record<string, string> = {
|
||||||
'ddim': 'DDIM',
|
'ddim': 'DDIM',
|
||||||
'ddpm': 'DDPM',
|
'ddpm': 'DDPM',
|
||||||
'dpm-multi': 'DPM Multistep',
|
'dpm-multi': 'DPM Multistep',
|
||||||
|
@ -32,12 +32,12 @@ export interface Txt2ImgProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Txt2Img(props: Txt2ImgProps) {
|
export function Txt2Img(props: Txt2ImgProps) {
|
||||||
const { client } = props;
|
const { client, model } = props;
|
||||||
|
|
||||||
async function generateImage() {
|
async function generateImage() {
|
||||||
return client.txt2img({
|
return client.txt2img({
|
||||||
...params,
|
...params,
|
||||||
model: props.model,
|
model,
|
||||||
prompt,
|
prompt,
|
||||||
scheduler,
|
scheduler,
|
||||||
});
|
});
|
||||||
|
@ -78,53 +78,19 @@ export function Txt2Img(props: Txt2ImgProps) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSchedulers() {
|
|
||||||
switch (schedulers.status) {
|
|
||||||
case 'error':
|
|
||||||
return <MenuItem value='error'>Error</MenuItem>;
|
|
||||||
case 'loading':
|
|
||||||
return <MenuItem value='loading'>Loading</MenuItem>;
|
|
||||||
case 'success':
|
|
||||||
return mustExist(schedulers.data).map((name) => <MenuItem key={name} value={name}>{SCHEDULER_NAMES[name]}</MenuItem>);
|
|
||||||
default:
|
|
||||||
return <MenuItem value='error'>Unknown Error</MenuItem>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderPlatforms() {
|
|
||||||
switch (platforms.status) {
|
|
||||||
case 'error':
|
|
||||||
return <MenuItem value='error'>Error</MenuItem>;
|
|
||||||
case 'loading':
|
|
||||||
return <MenuItem value='loading'>Loading</MenuItem>;
|
|
||||||
case 'success':
|
|
||||||
return mustExist(platforms.data).map((name) => <MenuItem key={name} value={name}>{PLATFORM_NAMES[name]}</MenuItem>);
|
|
||||||
default:
|
|
||||||
return <MenuItem value='error'>Unknown Error</MenuItem>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Box>
|
return <Box>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<Stack direction='row' spacing={2}>
|
<Stack direction='row' spacing={2}>
|
||||||
<Select
|
<QueryList result={schedulers} value={scheduler} labels={SCHEDULER_LABELS}
|
||||||
value={scheduler}
|
onChange={(value) => {
|
||||||
label="Scheduler"
|
setScheduler(value);
|
||||||
onChange={(event) => {
|
|
||||||
setScheduler(event.target.value);
|
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{renderSchedulers()}
|
<QueryList result={platforms} value={platform} labels={PLATFORM_LABELS}
|
||||||
</Select>
|
onChange={(value) => {
|
||||||
<Select
|
setPlatform(value);
|
||||||
value={platform}
|
|
||||||
label="Platform"
|
|
||||||
onChange={(event) => {
|
|
||||||
setPlatform(event.target.value);
|
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{renderPlatforms()}
|
|
||||||
</Select>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
<ImageControl params={params} onChange={(newParams) => {
|
<ImageControl params={params} onChange={(newParams) => {
|
||||||
setParams(newParams);
|
setParams(newParams);
|
||||||
|
|
|
@ -13,7 +13,7 @@ export interface Config {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadConfig() {
|
export async function loadConfig(): Promise<Config> {
|
||||||
const configPath = new URL('./config.json', window.origin);
|
const configPath = new URL('./config.json', window.origin);
|
||||||
const configReq = await fetch(configPath);
|
const configReq = await fetch(configPath);
|
||||||
if (configReq.status === STATUS_SUCCESS) {
|
if (configReq.status === STATUS_SUCCESS) {
|
||||||
|
@ -30,7 +30,9 @@ export async function main() {
|
||||||
|
|
||||||
const appElement = mustExist(document.getElementById('app'));
|
const appElement = mustExist(document.getElementById('app'));
|
||||||
const app = ReactDOM.createRoot(appElement);
|
const app = ReactDOM.createRoot(appElement);
|
||||||
app.render(<QueryClientProvider client={query}><OnnxWeb client={client} /></QueryClientProvider>);
|
app.render(<QueryClientProvider client={query}>
|
||||||
|
<OnnxWeb client={client} />
|
||||||
|
</QueryClientProvider>);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
|
|
Loading…
Reference in New Issue