feat(gui): add download and delete buttons to image history
This commit is contained in:
parent
4585792492
commit
e605c9f66b
|
@ -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
|
||||
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
|
||||
```
|
||||
|
||||
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
|
||||
> 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
|
||||
|
|
|
@ -14,6 +14,9 @@ from os import environ, makedirs, path, scandir
|
|||
import numpy as np
|
||||
|
||||
# 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_cfg = 8
|
||||
default_steps = 20
|
||||
|
@ -97,7 +100,8 @@ def load_pipeline(model, provider, scheduler):
|
|||
|
||||
if last_pipeline_scheduler != 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
|
||||
|
||||
return pipe
|
||||
|
@ -176,11 +180,11 @@ def txt2img():
|
|||
user = request.remote_addr
|
||||
|
||||
# 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',
|
||||
platform_providers, 'amd')
|
||||
platform_providers, default_platform)
|
||||
scheduler = get_from_map(request.args, 'scheduler',
|
||||
pipeline_schedulers, 'euler-a')
|
||||
pipeline_schedulers, default_scheduler)
|
||||
|
||||
# image params
|
||||
prompt = request.args.get('prompt', default_prompt)
|
||||
|
|
|
@ -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 { ApiResponse } from '../api/client.js';
|
||||
|
||||
export interface ImageCardProps {
|
||||
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) {
|
||||
const { value } = props;
|
||||
const { params, output } = value;
|
||||
|
||||
return <Card sx={{ maxWidth: params.width }}>
|
||||
return <Card sx={{ maxWidth: params.width }} elevation={2}>
|
||||
<CardMedia sx={{ height: params.height }}
|
||||
component='img'
|
||||
image={output}
|
||||
|
@ -20,24 +30,26 @@ export function ImageCard(props: ImageCardProps) {
|
|||
<CardContent>
|
||||
<Box>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={4}>
|
||||
<Paper>CFG: {params.cfg}</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Paper>Steps: {params.steps}</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Paper>Size: {params.width}x{params.height}</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Paper>Seed: {params.seed}</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={8}>
|
||||
<Paper>Scheduler: {params.scheduler}</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Paper>{params.prompt}</Paper>
|
||||
</Grid>
|
||||
<GridItem xs={4}>CFG: {params.cfg}</GridItem>
|
||||
<GridItem xs={4}>Steps: {params.steps}</GridItem>
|
||||
<GridItem xs={4}>Size: {params.width}x{params.height}</GridItem>
|
||||
<GridItem xs={4}>Seed: {params.seed}</GridItem>
|
||||
<GridItem xs={8}>Scheduler: {params.scheduler}</GridItem>
|
||||
<GridItem xs={12}>{params.prompt}</GridItem>
|
||||
<GridItem xs={2}>
|
||||
<Button onClick={() => window.open(output, '_blank')}>
|
||||
<Download />
|
||||
</Button>
|
||||
</GridItem>
|
||||
<GridItem xs={2}>
|
||||
<Button onClick={() => {
|
||||
if (doesExist(props.onDelete)) {
|
||||
props.onDelete(value);
|
||||
}
|
||||
}}>
|
||||
<Delete />
|
||||
</Button>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</Box>
|
||||
</CardContent>
|
||||
|
|
|
@ -4,12 +4,18 @@ import * as React from 'react';
|
|||
import { UseMutationResult } from 'react-query';
|
||||
import { LoadingCard } from './LoadingCard';
|
||||
|
||||
export interface MutationHistoryChildProps<T> {
|
||||
value: T;
|
||||
|
||||
onDelete: (key: T) => void;
|
||||
}
|
||||
|
||||
export interface MutationHistoryProps<T> {
|
||||
element: React.ComponentType<{value: T}>;
|
||||
element: React.ComponentType<MutationHistoryChildProps<T>>;
|
||||
limit: number;
|
||||
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>) {
|
||||
|
@ -25,7 +31,7 @@ export function MutationHistory<T>(props: MutationHistoryProps<T>) {
|
|||
|
||||
if (status === 'success') {
|
||||
const { data } = result;
|
||||
if (props.isPresent(history, data)) {
|
||||
if (history.some((other) => props.isEqual(data, other))) {
|
||||
// item already exists, skip it
|
||||
} else {
|
||||
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) {
|
||||
children.push(...history.map((item) => <props.element value={item} />));
|
||||
children.push(...history.map((item) => <props.element value={item} onDelete={removeHistory} />));
|
||||
} else {
|
||||
// only show the prompt when the button has not been pushed
|
||||
if (status !== 'loading') {
|
||||
|
|
|
@ -71,7 +71,7 @@ export function Txt2Img(props: Txt2ImgProps) {
|
|||
}} />
|
||||
<Button onClick={() => generate.mutate()}>Generate</Button>
|
||||
<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>
|
||||
</Box>;
|
||||
|
|
Loading…
Reference in New Issue