1
0
Fork 0

feat(gui): add download and delete buttons to image history

This commit is contained in:
Sean Sube 2023-01-06 22:20:14 -06:00
parent 4585792492
commit e605c9f66b
5 changed files with 58 additions and 31 deletions

View File

@ -299,7 +299,7 @@ If you have a good base or example prompt, you can set that in the config file:
} }
``` ```
When running the dev server, `node serve.js`, the config file at `out/config.json` will be loaded. If you want to load When running the dev server, `node serve.js`, the config file will be loaded from `out/config.json`. If you want to load
a different config file, save it to your home directory named `onnx-web-config.json` and copy it into the output a different config file, save it to your home directory named `onnx-web-config.json` and copy it into the output
directory after building the bundle: directory after building the bundle:
@ -307,7 +307,8 @@ directory after building the bundle:
> make bundle && cp -v ~/onnx-web-config.json out/config.json > make bundle && cp -v ~/onnx-web-config.json out/config.json
``` ```
When running the container, you can mount the config at `/usr/share/nginx/html/config.json` using: When running the container, the config will be loaded from `/usr/share/nginx/html/config.json` and you can mount a
custom config using:
```shell ```shell
> podman run -p 8000:80 --rm -v ~/onnx-web-config.json:/usr/share/nginx/html/config.json:ro docker.io/ssube/onnx-web-gui:main-nginx-bullseye > podman run -p 8000:80 --rm -v ~/onnx-web-config.json:/usr/share/nginx/html/config.json:ro docker.io/ssube/onnx-web-gui:main-nginx-bullseye

View File

@ -14,6 +14,9 @@ from os import environ, makedirs, path, scandir
import numpy as np import numpy as np
# defaults # defaults
default_model = "stable-diffusion-onnx-v1-5"
default_platform = "amd"
default_scheduler = "euler-a"
default_prompt = "a photo of an astronaut eating a hamburger" default_prompt = "a photo of an astronaut eating a hamburger"
default_cfg = 8 default_cfg = 8
default_steps = 20 default_steps = 20
@ -97,7 +100,8 @@ def load_pipeline(model, provider, scheduler):
if last_pipeline_scheduler != scheduler: if last_pipeline_scheduler != scheduler:
print('changing pipeline scheduler') print('changing pipeline scheduler')
pipe.scheduler = scheduler.from_pretrained(model, subfolder="scheduler") pipe.scheduler = scheduler.from_pretrained(
model, subfolder="scheduler")
last_pipeline_scheduler = scheduler last_pipeline_scheduler = scheduler
return pipe return pipe
@ -176,11 +180,11 @@ def txt2img():
user = request.remote_addr user = request.remote_addr
# pipeline stuff # pipeline stuff
model = safer_join(model_path, request.args.get('model', 'stable-diffusion-onnx-v1-5')) model = safer_join(model_path, request.args.get('model', default_model))
provider = get_from_map(request.args, 'platform', provider = get_from_map(request.args, 'platform',
platform_providers, 'amd') platform_providers, default_platform)
scheduler = get_from_map(request.args, 'scheduler', scheduler = get_from_map(request.args, 'scheduler',
pipeline_schedulers, 'euler-a') pipeline_schedulers, default_scheduler)
# image params # image params
prompt = request.args.get('prompt', default_prompt) prompt = request.args.get('prompt', default_prompt)

View File

@ -1,17 +1,27 @@
import { Box, Card, CardContent, CardMedia, Grid, Paper } from '@mui/material'; import { doesExist } from '@apextoaster/js-utils';
import { Delete, Download } from '@mui/icons-material';
import { Box, Button, Card, CardContent, CardMedia, Grid, Paper } from '@mui/material';
import * as React from 'react'; import * as React from 'react';
import { ApiResponse } from '../api/client.js'; import { ApiResponse } from '../api/client.js';
export interface ImageCardProps { export interface ImageCardProps {
value: ApiResponse; value: ApiResponse;
onDelete?: (key: ApiResponse) => void;
}
export function GridItem(props: {xs: number; children: React.ReactNode}) {
return <Grid item xs={props.xs}>
<Paper elevation={0} sx={{ padding: 1 }}>{props.children}</Paper>
</Grid>;
} }
export function ImageCard(props: ImageCardProps) { export function ImageCard(props: ImageCardProps) {
const { value } = props; const { value } = props;
const { params, output } = value; const { params, output } = value;
return <Card sx={{ maxWidth: params.width }}> return <Card sx={{ maxWidth: params.width }} elevation={2}>
<CardMedia sx={{ height: params.height }} <CardMedia sx={{ height: params.height }}
component='img' component='img'
image={output} image={output}
@ -20,24 +30,26 @@ export function ImageCard(props: ImageCardProps) {
<CardContent> <CardContent>
<Box> <Box>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={4}> <GridItem xs={4}>CFG: {params.cfg}</GridItem>
<Paper>CFG: {params.cfg}</Paper> <GridItem xs={4}>Steps: {params.steps}</GridItem>
</Grid> <GridItem xs={4}>Size: {params.width}x{params.height}</GridItem>
<Grid item xs={4}> <GridItem xs={4}>Seed: {params.seed}</GridItem>
<Paper>Steps: {params.steps}</Paper> <GridItem xs={8}>Scheduler: {params.scheduler}</GridItem>
</Grid> <GridItem xs={12}>{params.prompt}</GridItem>
<Grid item xs={4}> <GridItem xs={2}>
<Paper>Size: {params.width}x{params.height}</Paper> <Button onClick={() => window.open(output, '_blank')}>
</Grid> <Download />
<Grid item xs={4}> </Button>
<Paper>Seed: {params.seed}</Paper> </GridItem>
</Grid> <GridItem xs={2}>
<Grid item xs={8}> <Button onClick={() => {
<Paper>Scheduler: {params.scheduler}</Paper> if (doesExist(props.onDelete)) {
</Grid> props.onDelete(value);
<Grid item xs={12}> }
<Paper>{params.prompt}</Paper> }}>
</Grid> <Delete />
</Button>
</GridItem>
</Grid> </Grid>
</Box> </Box>
</CardContent> </CardContent>

View File

@ -4,12 +4,18 @@ import * as React from 'react';
import { UseMutationResult } from 'react-query'; import { UseMutationResult } from 'react-query';
import { LoadingCard } from './LoadingCard'; import { LoadingCard } from './LoadingCard';
export interface MutationHistoryChildProps<T> {
value: T;
onDelete: (key: T) => void;
}
export interface MutationHistoryProps<T> { export interface MutationHistoryProps<T> {
element: React.ComponentType<{value: T}>; element: React.ComponentType<MutationHistoryChildProps<T>>;
limit: number; limit: number;
result: UseMutationResult<T, unknown, void>; result: UseMutationResult<T, unknown, void>;
isPresent: (list: Array<T>, item: T) => boolean; isEqual: (a: T, b: T) => boolean;
} }
export function MutationHistory<T>(props: MutationHistoryProps<T>) { export function MutationHistory<T>(props: MutationHistoryProps<T>) {
@ -25,7 +31,7 @@ export function MutationHistory<T>(props: MutationHistoryProps<T>) {
if (status === 'success') { if (status === 'success') {
const { data } = result; const { data } = result;
if (props.isPresent(history, data)) { if (history.some((other) => props.isEqual(data, other))) {
// item already exists, skip it // item already exists, skip it
} else { } else {
setHistory([ setHistory([
@ -35,8 +41,12 @@ export function MutationHistory<T>(props: MutationHistoryProps<T>) {
} }
} }
function removeHistory(data: T) {
setHistory(history.filter((item) => props.isEqual(item, data) === false));
}
if (history.length > 0) { if (history.length > 0) {
children.push(...history.map((item) => <props.element value={item} />)); children.push(...history.map((item) => <props.element value={item} onDelete={removeHistory} />));
} else { } else {
// only show the prompt when the button has not been pushed // only show the prompt when the button has not been pushed
if (status !== 'loading') { if (status !== 'loading') {

View File

@ -71,7 +71,7 @@ export function Txt2Img(props: Txt2ImgProps) {
}} /> }} />
<Button onClick={() => generate.mutate()}>Generate</Button> <Button onClick={() => generate.mutate()}>Generate</Button>
<MutationHistory result={generate} limit={4} element={ImageCard} <MutationHistory result={generate} limit={4} element={ImageCard}
isPresent={(list, item) => list.some((other) => item.output === other.output)} isEqual={(a, b) => a.output === b.output}
/> />
</Stack> </Stack>
</Box>; </Box>;