mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
Merge branch 'development' into development
This commit is contained in:
17
frontend/src/app/App.scss
Normal file
17
frontend/src/app/App.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
@use '../styles/Mixins/' as *;
|
||||
|
||||
.App {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
display: grid;
|
||||
row-gap: 1rem;
|
||||
margin: 0.6rem;
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--background-color);
|
||||
grid-auto-rows: max-content;
|
||||
width: $app-width;
|
||||
height: $app-height;
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
import { Grid, GridItem } from '@chakra-ui/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import CurrentImageDisplay from '../features/gallery/CurrentImageDisplay';
|
||||
import ImageGallery from '../features/gallery/ImageGallery';
|
||||
import ProgressBar from '../features/system/ProgressBar';
|
||||
import SiteHeader from '../features/system/SiteHeader';
|
||||
import OptionsAccordion from '../features/options/OptionsAccordion';
|
||||
import ProcessButtons from '../features/options/ProcessButtons';
|
||||
import PromptInput from '../features/options/PromptInput';
|
||||
import LogViewer from '../features/system/LogViewer';
|
||||
import Console from '../features/system/Console';
|
||||
import Loading from '../Loading';
|
||||
import { useAppDispatch } from './store';
|
||||
import { requestSystemConfig } from './socketio/actions';
|
||||
import { keepGUIAlive } from './utils';
|
||||
import InvokeTabs from '../features/tabs/InvokeTabs';
|
||||
|
||||
keepGUIAlive();
|
||||
|
||||
const App = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -22,43 +20,14 @@ const App = () => {
|
||||
}, [dispatch]);
|
||||
|
||||
return isReady ? (
|
||||
<>
|
||||
<Grid
|
||||
width="100vw"
|
||||
height="100vh"
|
||||
templateAreas={`
|
||||
"header header header header"
|
||||
"progressBar progressBar progressBar progressBar"
|
||||
"menu prompt processButtons imageRoll"
|
||||
"menu currentImage currentImage imageRoll"`}
|
||||
gridTemplateRows={'36px 10px 100px auto'}
|
||||
gridTemplateColumns={'350px auto 100px 388px'}
|
||||
gap={2}
|
||||
>
|
||||
<GridItem area={'header'} pt={1}>
|
||||
<SiteHeader />
|
||||
</GridItem>
|
||||
<GridItem area={'progressBar'}>
|
||||
<ProgressBar />
|
||||
</GridItem>
|
||||
<GridItem pl="2" area={'menu'} overflowY="scroll">
|
||||
<OptionsAccordion />
|
||||
</GridItem>
|
||||
<GridItem area={'prompt'}>
|
||||
<PromptInput />
|
||||
</GridItem>
|
||||
<GridItem area={'processButtons'}>
|
||||
<ProcessButtons />
|
||||
</GridItem>
|
||||
<GridItem area={'currentImage'}>
|
||||
<CurrentImageDisplay />
|
||||
</GridItem>
|
||||
<GridItem pr="2" area={'imageRoll'} overflowY="scroll">
|
||||
<ImageGallery />
|
||||
</GridItem>
|
||||
</Grid>
|
||||
<LogViewer />
|
||||
</>
|
||||
<div className="App">
|
||||
<ProgressBar />
|
||||
<div className="app-content">
|
||||
<SiteHeader />
|
||||
<InvokeTabs />
|
||||
</div>
|
||||
<Console />
|
||||
</div>
|
||||
) : (
|
||||
<Loading />
|
||||
);
|
||||
|
||||
@@ -17,13 +17,15 @@ export const SAMPLERS: Array<string> = [
|
||||
// Valid image widths
|
||||
export const WIDTHS: Array<number> = [
|
||||
64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960,
|
||||
1024,
|
||||
1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792,
|
||||
1856, 1920, 1984, 2048,
|
||||
];
|
||||
|
||||
// Valid image heights
|
||||
export const HEIGHTS: Array<number> = [
|
||||
64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960,
|
||||
1024,
|
||||
1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792,
|
||||
1856, 1920, 1984, 2048,
|
||||
];
|
||||
|
||||
// Valid upscaling levels
|
||||
|
||||
@@ -7,12 +7,12 @@ type FeatureHelpInfo = {
|
||||
export enum Feature {
|
||||
PROMPT,
|
||||
GALLERY,
|
||||
OUTPUT,
|
||||
SEED_AND_VARIATION,
|
||||
ESRGAN,
|
||||
OTHER,
|
||||
SEED,
|
||||
VARIATIONS,
|
||||
UPSCALE,
|
||||
FACE_CORRECTION,
|
||||
IMAGE_TO_IMAGE,
|
||||
SAMPLER,
|
||||
}
|
||||
|
||||
export const FEATURES: Record<Feature, FeatureHelpInfo> = {
|
||||
@@ -26,18 +26,23 @@ export const FEATURES: Record<Feature, FeatureHelpInfo> = {
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.OUTPUT]: {
|
||||
text: 'The Height and Width of generations can be controlled here. If you experience errors, you may be generating an image too large for your system. The seamless option will more often result in repeating patterns in outputs.',
|
||||
[Feature.OTHER]: {
|
||||
text: 'Additional Options',
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.SEED_AND_VARIATION]: {
|
||||
text: 'Seed values provide an initial set of noise which guide the denoising process. Try a variation with an amount of between 0 and 1 to change the output image for that seed.',
|
||||
[Feature.SEED]: {
|
||||
text: 'Seed values provide an initial set of noise which guide the denoising process.',
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.ESRGAN]: {
|
||||
text: 'The ESRGAN setting can be used to increase the output resolution without requiring a higher width/height in the initial generation.',
|
||||
[Feature.VARIATIONS]: {
|
||||
text: 'Try a variation with an amount of between 0 and 1 to change the output image for the set seed.',
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.UPSCALE]: {
|
||||
text: 'Using ESRGAN you can increase the output resolution without requiring a higher width/height in the initial generation.',
|
||||
href: 'link/to/docs/feature1.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
@@ -51,9 +56,4 @@ export const FEATURES: Record<Feature, FeatureHelpInfo> = {
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.SAMPLER]: {
|
||||
text: 'This setting allows for different denoising samplers to be used, as well as the number of denoising steps used, which will change the resulting output.',
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
};
|
||||
|
||||
7
frontend/src/app/invokeai.d.ts
vendored
7
frontend/src/app/invokeai.d.ts
vendored
@@ -129,6 +129,7 @@ export declare type SystemStatus = {
|
||||
totalIterations: number;
|
||||
currentStatus: string;
|
||||
currentStatusHasSteps: boolean;
|
||||
hasError: boolean;
|
||||
};
|
||||
|
||||
export declare type SystemConfig = {
|
||||
@@ -159,10 +160,8 @@ export declare type ErrorResponse = {
|
||||
};
|
||||
|
||||
export declare type GalleryImagesResponse = {
|
||||
images: Array<Omit<Image, 'uuid'>>;
|
||||
nextPage: number;
|
||||
offset: number;
|
||||
onlyNewImages: boolean;
|
||||
images: Array<Omit<Image, 'uuid'>>;
|
||||
areMoreImagesAvailable: boolean;
|
||||
};
|
||||
|
||||
export declare type ImageUrlAndUuidResponse = {
|
||||
|
||||
@@ -50,7 +50,10 @@ const makeSocketIOEmitters = (
|
||||
const esrganParameters = {
|
||||
upscale: [upscalingLevel, upscalingStrength],
|
||||
};
|
||||
socketio.emit('runESRGAN', imageToProcess, esrganParameters);
|
||||
socketio.emit('runPostprocessing', imageToProcess, {
|
||||
type: 'esrgan',
|
||||
...esrganParameters,
|
||||
});
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
@@ -68,7 +71,10 @@ const makeSocketIOEmitters = (
|
||||
const gfpganParameters = {
|
||||
gfpgan_strength: gfpganStrength,
|
||||
};
|
||||
socketio.emit('runGFPGAN', imageToProcess, gfpganParameters);
|
||||
socketio.emit('runPostprocessing', imageToProcess, {
|
||||
type: 'gfpgan',
|
||||
...gfpganParameters,
|
||||
});
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
@@ -84,16 +90,12 @@ const makeSocketIOEmitters = (
|
||||
socketio.emit('deleteImage', url, uuid);
|
||||
},
|
||||
emitRequestImages: () => {
|
||||
const { nextPage, offset } = getState().gallery;
|
||||
socketio.emit('requestImages', nextPage, offset);
|
||||
const { earliest_mtime } = getState().gallery;
|
||||
socketio.emit('requestImages', earliest_mtime);
|
||||
},
|
||||
emitRequestNewImages: () => {
|
||||
const { nextPage, offset, images } = getState().gallery;
|
||||
if (images.length > 0) {
|
||||
socketio.emit('requestImages', nextPage, offset, images[0].mtime);
|
||||
} else {
|
||||
socketio.emit('requestImages', nextPage, offset);
|
||||
}
|
||||
const { latest_mtime } = getState().gallery;
|
||||
socketio.emit('requestLatestImages', latest_mtime);
|
||||
},
|
||||
emitCancelProcessing: () => {
|
||||
socketio.emit('cancel');
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
setSystemStatus,
|
||||
setCurrentStatus,
|
||||
setSystemConfig,
|
||||
processingCanceled,
|
||||
errorOccurred,
|
||||
} from '../../features/system/systemSlice';
|
||||
|
||||
import {
|
||||
@@ -25,7 +27,7 @@ import {
|
||||
setInitialImagePath,
|
||||
setMaskPath,
|
||||
} from '../../features/options/optionsSlice';
|
||||
import { requestNewImages } from './actions';
|
||||
import { requestImages, requestNewImages } from './actions';
|
||||
|
||||
/**
|
||||
* Returns an object containing listener callbacks for socketio events.
|
||||
@@ -44,7 +46,11 @@ const makeSocketIOListeners = (
|
||||
try {
|
||||
dispatch(setIsConnected(true));
|
||||
dispatch(setCurrentStatus('Connected'));
|
||||
dispatch(requestNewImages());
|
||||
if (getState().gallery.latest_mtime) {
|
||||
dispatch(requestNewImages());
|
||||
} else {
|
||||
dispatch(requestImages());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -90,7 +96,6 @@ const makeSocketIOListeners = (
|
||||
message: `Image generated: ${url}`,
|
||||
})
|
||||
);
|
||||
dispatch(setIsProcessing(false));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -116,7 +121,6 @@ const makeSocketIOListeners = (
|
||||
message: `Intermediate image generated: ${url}`,
|
||||
})
|
||||
);
|
||||
dispatch(setIsProcessing(false));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -124,7 +128,7 @@ const makeSocketIOListeners = (
|
||||
/**
|
||||
* Callback to run when we receive an 'esrganResult' event.
|
||||
*/
|
||||
onESRGANResult: (data: InvokeAI.ImageResultResponse) => {
|
||||
onPostprocessingResult: (data: InvokeAI.ImageResultResponse) => {
|
||||
try {
|
||||
const { url, metadata, mtime } = data;
|
||||
|
||||
@@ -140,10 +144,9 @@ const makeSocketIOListeners = (
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Upscaled: ${url}`,
|
||||
message: `Postprocessed: ${url}`,
|
||||
})
|
||||
);
|
||||
dispatch(setIsProcessing(false));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -204,7 +207,7 @@ const makeSocketIOListeners = (
|
||||
level: 'error',
|
||||
})
|
||||
);
|
||||
dispatch(setIsProcessing(false));
|
||||
dispatch(errorOccurred());
|
||||
dispatch(clearIntermediateImage());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
@@ -214,7 +217,7 @@ const makeSocketIOListeners = (
|
||||
* Callback to run when we receive a 'galleryImages' event.
|
||||
*/
|
||||
onGalleryImages: (data: InvokeAI.GalleryImagesResponse) => {
|
||||
const { images, nextPage, offset } = data;
|
||||
const { images, areMoreImagesAvailable } = data;
|
||||
|
||||
/**
|
||||
* the logic here ideally would be in the reducer but we have a side effect:
|
||||
@@ -232,7 +235,9 @@ const makeSocketIOListeners = (
|
||||
};
|
||||
});
|
||||
|
||||
dispatch(addGalleryImages({ images: preparedImages, nextPage, offset }));
|
||||
dispatch(
|
||||
addGalleryImages({ images: preparedImages, areMoreImagesAvailable })
|
||||
);
|
||||
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
@@ -245,7 +250,7 @@ const makeSocketIOListeners = (
|
||||
* Callback to run when we receive a 'processingCanceled' event.
|
||||
*/
|
||||
onProcessingCanceled: () => {
|
||||
dispatch(setIsProcessing(false));
|
||||
dispatch(processingCanceled());
|
||||
|
||||
const { intermediateImage } = getState().gallery;
|
||||
|
||||
@@ -259,6 +264,7 @@ const makeSocketIOListeners = (
|
||||
);
|
||||
dispatch(clearIntermediateImage());
|
||||
}
|
||||
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
@@ -273,6 +279,17 @@ const makeSocketIOListeners = (
|
||||
onImageDeleted: (data: InvokeAI.ImageUrlAndUuidResponse) => {
|
||||
const { url, uuid } = data;
|
||||
dispatch(removeImage(uuid));
|
||||
|
||||
const { initialImagePath, maskPath } = getState().options;
|
||||
|
||||
if (initialImagePath === url) {
|
||||
dispatch(setInitialImagePath(''));
|
||||
}
|
||||
|
||||
if (maskPath === url) {
|
||||
dispatch(setMaskPath(''));
|
||||
}
|
||||
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
|
||||
@@ -35,8 +35,7 @@ export const socketioMiddleware = () => {
|
||||
onConnect,
|
||||
onDisconnect,
|
||||
onError,
|
||||
onESRGANResult,
|
||||
onGFPGANResult,
|
||||
onPostprocessingResult,
|
||||
onGenerationResult,
|
||||
onIntermediateResult,
|
||||
onProgressUpdate,
|
||||
@@ -76,12 +75,9 @@ export const socketioMiddleware = () => {
|
||||
onGenerationResult(data)
|
||||
);
|
||||
|
||||
socketio.on('esrganResult', (data: InvokeAI.ImageResultResponse) =>
|
||||
onESRGANResult(data)
|
||||
);
|
||||
|
||||
socketio.on('gfpganResult', (data: InvokeAI.ImageResultResponse) =>
|
||||
onGFPGANResult(data)
|
||||
socketio.on(
|
||||
'postprocessingResult',
|
||||
(data: InvokeAI.ImageResultResponse) => onPostprocessingResult(data)
|
||||
);
|
||||
|
||||
socketio.on('intermediateResult', (data: InvokeAI.ImageResultResponse) =>
|
||||
@@ -153,7 +149,6 @@ export const socketioMiddleware = () => {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 'socketio/cancelProcessing': {
|
||||
emitCancelProcessing();
|
||||
break;
|
||||
|
||||
@@ -7,6 +7,7 @@ import storage from 'redux-persist/lib/storage'; // defaults to localStorage for
|
||||
|
||||
import optionsReducer from '../features/options/optionsSlice';
|
||||
import galleryReducer from '../features/gallery/gallerySlice';
|
||||
|
||||
import systemReducer from '../features/system/systemSlice';
|
||||
import { socketioMiddleware } from './socketio/middleware';
|
||||
|
||||
|
||||
25
frontend/src/app/utils.ts
Normal file
25
frontend/src/app/utils.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export function keepGUIAlive() {
|
||||
async function getRequest(url = '') {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
cache: 'no-cache',
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
const keepAliveServer = () => {
|
||||
const url = document.location;
|
||||
const route = '/flaskwebgui-keep-server-alive';
|
||||
getRequest(url + route).then((data) => {
|
||||
return data;
|
||||
});
|
||||
};
|
||||
|
||||
if (!import.meta.env.NODE_ENV || import.meta.env.NODE_ENV === 'production') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const intervalRequest = 3 * 1000;
|
||||
keepAliveServer();
|
||||
setInterval(keepAliveServer, intervalRequest);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user