Skip to content

Commit

Permalink
feat(gui): add download and delete buttons to image history
Browse files Browse the repository at this point in the history
  • Loading branch information
ssube committed Jan 7, 2023
1 parent 4585792 commit e605c9f
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 31 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,15 +299,16 @@ 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:

```shell
> 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
Expand Down
12 changes: 8 additions & 4 deletions api/onnx_web/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
52 changes: 32 additions & 20 deletions gui/src/components/ImageCard.tsx
Original file line number Diff line number Diff line change
@@ -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}
Expand All @@ -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>
Expand Down
18 changes: 14 additions & 4 deletions gui/src/components/MutationHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>) {
Expand All @@ -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([
Expand All @@ -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') {
Expand Down
2 changes: 1 addition & 1 deletion gui/src/components/Txt2Img.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down

0 comments on commit e605c9f

Please sign in to comment.