diff --git a/gui/src/api/client.ts b/gui/src/api/client.ts index 8c15e7f5..642d626d 100644 --- a/gui/src/api/client.ts +++ b/gui/src/api/client.ts @@ -1,4 +1,4 @@ -import { doesExist } from '@apextoaster/js-utils'; +import { doesExist, NotImplementedError } from '@apextoaster/js-utils'; export interface BaseImgParams { /** @@ -38,6 +38,17 @@ export interface Txt2ImgParams extends BaseImgParams { export type Txt2ImgResponse = Required; +export interface InpaintParams extends Img2ImgParams { + mask: Blob; +} + +export interface OutpaintParams extends Img2ImgParams { + up: boolean; + down: boolean; + left: boolean; + right: boolean; +} + export interface ApiResponse { output: string; params: Txt2ImgResponse; @@ -48,8 +59,11 @@ export interface ApiClient { platforms(): Promise>; schedulers(): Promise>; - img2img(params: Img2ImgParams): Promise; // TODO: slightly different response type + img2img(params: Img2ImgParams): Promise; txt2img(params: Txt2ImgParams): Promise; + + inpaint(params: InpaintParams): Promise; + outpaint(params: OutpaintParams): Promise; } export const STATUS_SUCCESS = 200; @@ -165,5 +179,29 @@ export function makeClient(root: string, f = fetch): ApiClient { // eslint-disable-next-line no-return-await return await pending; }, + async inpaint(params: InpaintParams) { + if (doesExist(pending)) { + return pending; + } + + const url = makeImageURL(root, 'inpaint', params); + + const body = new FormData(); + body.append('mask', params.mask, 'mask'); + body.append('source', params.source, 'source'); + + pending = f(url, { + body, + method: 'POST', + }).then((res) => imageFromResponse(root, res)).finally(() => { + pending = undefined; + }); + + // eslint-disable-next-line no-return-await + return await pending; + }, + async outpaint() { + throw new NotImplementedError(); + }, }; } diff --git a/gui/src/components/Inpaint.tsx b/gui/src/components/Inpaint.tsx index 3d5b9102..35223669 100644 --- a/gui/src/components/Inpaint.tsx +++ b/gui/src/components/Inpaint.tsx @@ -3,7 +3,7 @@ import { Box, Button, Stack } from '@mui/material'; import * as React from 'react'; import { useMutation, useQuery } from 'react-query'; -import { ApiClient, BaseImgParams } from '../api/client.js'; +import { ApiClient, ApiResponse, BaseImgParams } from '../api/client.js'; import { Config, CONFIG_DEFAULTS, STALE_TIME } from '../config.js'; import { SCHEDULER_LABELS } from '../strings.js'; import { ImageCard } from './ImageCard.js'; @@ -26,13 +26,20 @@ export function Inpaint(props: InpaintProps) { const { client, config, model, platform } = props; async function uploadSource() { - return client.img2img({ - ...params, - model, - platform, - scheduler, - strength, - source: mustExist(source), // TODO: show an error if this doesn't exist + const canvas = mustExist(canvasRef.current); + return new Promise((res, _rej) => { + canvas.toBlob((value) => { + const mask = mustExist(value); + res(client.inpaint({ + ...params, + model, + platform, + scheduler, + strength, + mask, + source: mustExist(source), + })); + }); }); }