decent queue display

This commit is contained in:
caranicas 2022-09-29 12:54:13 -04:00
parent fd2961ecb9
commit 129839ce21
21 changed files with 426 additions and 173 deletions

View File

@ -1,41 +0,0 @@
import React from "react";
import { doStopImage } from "../../../api";
import { useImageQueue } from "../../../stores/imageQueueStore";
import { BrandedButton } from "../../../styles/shared.css";
import { useCreateUI } from "../../../components/organisms/creationPanel/creationPanelUIStore";
export default function ClearQueue() {
const hasQueue = useImageQueue((state) => state.hasQueuedImages());
const clearQueue = useImageQueue((state) => state.clearQueue);
const showQueue = useCreateUI((state) => state.showQueue);
const toggleQueue = useCreateUI((state) => state.toggleQueue);
const stopAll = async () => {
console.log("stopAll");
try {
clearQueue();
const res = await doStopImage();
} catch (e) {
console.log(e);
}
};
return (
<>
<button className={BrandedButton} disabled={!hasQueue} onClick={() => void stopAll()}>Clear Queue</button>
<label>
<input
type="checkbox"
checked={showQueue}
onChange={() => toggleQueue()}
>
</input>
Display
</label>
</>
);
}

View File

@ -0,0 +1,25 @@
import React from "react";
import { doStopImage } from "../../../api";
import { useRequestQueue } from "../../../stores/requestQueueStore";
import { BrandedButton } from "../../../styles/shared.css";
export default function ClearQueue() {
const hasQueue = useRequestQueue((state) => state.hasPendingQueue());
const clearQueue = useRequestQueue((state) => state.clearQueue);
const stopAll = async () => {
try {
clearQueue();
const res = await doStopImage();
} catch (e) {
console.log(e);
}
};
return (
<button className={BrandedButton} disabled={!hasQueue} onClick={() => void stopAll()}>
Stop ALL
</button>
);
}

View File

@ -2,7 +2,11 @@
import React, { useEffect, useRef } from "react";
import { useImageCreate } from "../../../stores/imageCreateStore";
import { useImageQueue } from "../../../stores/imageQueueStore";
import {
QueueStatus,
useRequestQueue
} from "../../../stores/requestQueueStore";
import {
FetchingStates,
useImageFetching
@ -42,10 +46,10 @@ export default function MakeButton() {
const isSoundEnabled = useImageCreate((state) => state.isSoundEnabled());
const addNewImage = useImageQueue((state) => state.addNewImage);
const hasQueue = useImageQueue((state) => state.hasQueuedImages());
const removeFirstInQueue = useImageQueue((state) => state.removeFirstInQueue);
const { id, options } = useImageQueue((state) => state.firstInQueue());
const addtoQueue = useRequestQueue((state) => state.addtoQueue);
const hasQueue = useRequestQueue((state) => state.hasPendingQueue());
const { id, options } = useRequestQueue((state) => state.firstInQueue());
const updateQueueStatus = useRequestQueue((state) => state.updateStatus);
const status = useImageFetching((state) => state.status);
const setStatus = useImageFetching((state) => state.setStatus);
@ -64,7 +68,11 @@ export default function MakeButton() {
try {
const parsed = JSON.parse(jsonStr);
const { status, request, output: outputs } = parsed as ImageReturnType;
if (status === 'succeeded') {
updateQueueStatus(id, QueueStatus.complete);
outputs.forEach((output: any, index: number) => {
const { data, seed } = output as ImageOutput;
@ -79,10 +87,12 @@ export default function MakeButton() {
else {
console.warn(`Unexpected status: ${status}`);
updateQueueStatus(id, QueueStatus.error);
}
}
catch (e) {
updateQueueStatus(id, QueueStatus.error);
console.log("Error HACKING JSON: ", e)
}
}
@ -143,7 +153,7 @@ export default function MakeButton() {
}
}
catch (e) {
console.log('EXPECTED PARSE ERRROR')
// console.log('EXPECTED PARSE ERRROR')
finalJSON += jsonStr;
}
@ -152,8 +162,8 @@ export default function MakeButton() {
const startStream = async (id: string, req: ImageRequest) => {
try {
updateQueueStatus(id, QueueStatus.processing);
resetForFetching();
const res = await doMakeImage(req);
const reader = res.body?.getReader();
@ -164,6 +174,7 @@ export default function MakeButton() {
} catch (e) {
console.log('TOP LINE STREAM ERROR')
updateQueueStatus(id, QueueStatus.error);
console.log(e);
}
@ -201,7 +212,7 @@ export default function MakeButton() {
seed = useRandomSeed();
}
// add the request to the queue
addNewImage(uuidv4(), {
addtoQueue(uuidv4(), {
...req,
// updated the number of images to make
num_outputs: num,
@ -224,7 +235,7 @@ export default function MakeButton() {
useEffect(() => {
const makeImages = async (options: ImageRequest) => {
removeFirstInQueue();
// removeFirstInQueue();
await startStream(id ?? "", options);
}
@ -238,6 +249,7 @@ export default function MakeButton() {
console.log('req is undefined');
return;
}
makeImages(options).catch((e) => {
console.log('HAS QUEUE ERROR');
console.log(e);

View File

@ -1,7 +1,7 @@
import React from "react";
import { useImageCreate } from "../../../../../stores/imageCreateStore";
import ModifierTag from "../../../../atoms/modifierTag";
import ModifierTag from "../../../../molecules/modifierTag";
export default function ActiveTags() {
const selectedtags = useImageCreate((state) => state.selectedTags());

View File

@ -1,8 +1,10 @@
import React from "react";
import MakeButton from "../../../../atoms/makeButton";
import StopButton from "../../../../atoms/stopButton";
import ClearQueue from "../../../../atoms/clearQueue";
import MakeButton from "../../../../molecules/makeButton";
// import StopButton from "../../../../molecules/stopButton";
// import ClearQueue from "../../../../molecules/clearQueue";
import ShowQueue from "../showQueue";
import {
StopContainer
@ -12,10 +14,11 @@ export default function CreationActions() {
return (
<div>
<MakeButton></MakeButton>
<div className={StopContainer}>
<ShowQueue></ShowQueue>
{/* <div className={StopContainer}>
<StopButton></StopButton>
<ClearQueue></ClearQueue>
</div>
</div> */}
</div>
);
}

View File

@ -37,12 +37,6 @@ export default function BasicCreation() {
<CreationActions></CreationActions>
{/* <MakeButton></MakeButton>
<div>
<StopButton></StopButton>
<ClearQueue></ClearQueue>
</div> */}
<SeedImage></SeedImage>
<ActiveTags></ActiveTags>
</div>

View File

@ -0,0 +1,21 @@
import React from "react";
import { useCreateUI } from "../../creationPanelUIStore";
export default function ShowQueue() {
const showQueue = useCreateUI((state) => state.showQueue);
const toggleQueue = useCreateUI((state) => state.toggleQueue);
return (
<label>
<input
type="checkbox"
checked={showQueue}
onChange={() => toggleQueue()}
>
</input>
Display
</label>
);
}

View File

@ -1,11 +1,12 @@
import { style } from "@vanilla-extract/css";
import { PanelBox } from "../../../styles/shared.css";
export const CreationPaneMain = style({
position: "relative",
width: "100%",
height: "100%",
padding: "0 10px",
overflowY: "auto",
overflowX: "hidden",
});
export const InpaintingSlider = style({
@ -16,10 +17,11 @@ export const InpaintingSlider = style({
backgroundColor: "rgba(0, 0, 0, 0.5)",
});
export const QueueSlider = style({
export const QueueSlider = style([PanelBox, {
position: "absolute",
top: "10px",
left: "400px",
zIndex: 1,
backgroundColor: "rgba(0, 0, 0, 0.5)",
});
maxHeight: "90%",
overflowY: "auto",
}]);

View File

@ -15,7 +15,7 @@ import {
import { ModifierObject, useImageCreate } from "../../../../stores/imageCreateStore";
import { useCreateUI } from "../creationPanelUIStore";
import ModifierTag from "../../../atoms/modifierTag";
import ModifierTag from "../../../molecules/modifierTag";
interface ModifierListProps {
category: string;

View File

@ -11,7 +11,7 @@ import QueueDisplay from "../queueDisplay";
// import { useImageCreate } from "@stores/imageCreateStore.ts";
import { useImageCreate } from "../../../stores/imageCreateStore";
import { useImageQueue } from "../../../stores/imageQueueStore";
import { useRequestQueue } from "../../../stores/requestQueueStore";
import { useCreateUI } from "./creationPanelUIStore";
@ -24,12 +24,11 @@ import {
} from "./creationPanel.css";
export default function CreationPanel() {
const isInPaintingMode = useImageCreate((state) => state.isInpainting);
const showQueue = useCreateUI((state) => state.showQueue);
const hasQueue = useImageQueue((state) => state.hasQueuedImages());
const hasQueue = useRequestQueue((state) => state.hasAnyQueue());
// console.log('hasQueue', hasQueue);
console.log('showQueue', showQueue);
@ -48,7 +47,7 @@ export default function CreationPanel() {
</div>
)}
{(showQueue) && (
{(showQueue && hasQueue) && (
<div className={QueueSlider}>
<QueueDisplay></QueueDisplay>
</div>

View File

@ -1,24 +1,45 @@
import React from "react";
import { ImageRequest } from "../../../api";
import { useImageQueue } from "../../../stores/imageQueueStore";
import { QueuedRequest, useRequestQueue } from "../../../stores/requestQueueStore";
import {
QueueDisplayMain
QueueDisplayMain,
QueueListButtons,
CompleteButtton,
ErrorButton
} from "./queueDisplay.css";
import ClearQueue from "../../molecules/clearQueue";
import QueueItem from "./queueItem";
export default function QueueDisplay() {
const images = useImageQueue((state) => state.images);
console.log('images', images);
const requests: QueuedRequest[] = useRequestQueue((state) => state.requests);
const removeCompleted = useRequestQueue((state) => state.removeCompleted);
const removeErrored = useRequestQueue((state) => state.removeErrored);
const clearCompleted = () => {
console.log('clear completed');
removeCompleted();
}
const clearErrored = () => {
console.log('clear errored');
removeErrored();
}
return (
<div className={QueueDisplayMain}>
{images.map((image) => {
return <QueueItem key={image.id} info={image}></QueueItem>;
<ClearQueue />
<div className={QueueListButtons}>
<button className={CompleteButtton} onClick={clearCompleted}>Clear Completed</button>
<button className={ErrorButton} onClick={clearErrored}>Clear Errored</button>
</div>
{requests.map((request) => {
return <QueueItem key={request.id} request={request}></QueueItem>;
})}
</div>
);

View File

@ -1,10 +1,39 @@
import { style } from "@vanilla-extract/css";
import { style, globalStyle } from "@vanilla-extract/css";
import { BrandedButton } from "../../../styles/shared.css";
import { vars } from "../../../styles/theme/index.css";
export const QueueDisplayMain = style({
display: "flex",
flexDirection: "column",
width: '400px',
height: '100%',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
});
export const QueueListButtons = style({
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: vars.spacing.medium,
marginTop: vars.spacing.medium,
});
globalStyle(`${QueueListButtons} button`, {
flexGrow: 1,
});
globalStyle(`${QueueListButtons} button:first-child`, {
marginRight: vars.spacing.medium,
});
// TODO these should be a button recipe?
export const CompleteButtton = style([BrandedButton, {
}]);
export const ErrorButton = style([BrandedButton, {
}]);

View File

@ -1,20 +1,37 @@
import React from "react";
import {
QueueItemMain
} from "./queueItem.css";
import {
ImageRequest
} from '../../../../api'
import { QueueStatus, QueuedRequest, useRequestQueue } from '../../../../stores/requestQueueStore';
import StopButton from '../../../molecules/stopButton';
import {
QueueItemMain,
QueueButtons,
CompleteButtton,
PauseButton,
ResumeButton,
CancelButton,
RetryButton,
SendToTopButton,
} from "./queueItem.css";
interface QueueItemProps {
info: ImageRequest
request: QueuedRequest;
}
export default function QueueItem({ info }: QueueItemProps) {
export default function QueueItem({ request }: QueueItemProps) {
console.log('info', info);
// console.log('info', info);
// console.log('status', status);
const removeItem = useRequestQueue((state) => state.removeItem);
const updateStatus = useRequestQueue((state) => state.updateStatus);
const sendPendingToTop = useRequestQueue((state) => state.sendPendingToTop);
const {
id,
@ -23,16 +40,64 @@ export default function QueueItem({ info }: QueueItemProps) {
seed,
sampler,
},
// status,
} = info;
status,
} = request;
const removeFromQueue = () => {
console.log('remove from queue');
removeItem(id);
}
const pauseItem = () => {
console.log('pause item');
updateStatus(id, QueueStatus.paused);
}
const retryRequest = () => {
console.log('retry request');
updateStatus(id, QueueStatus.pending);
}
const sendToTop = () => {
console.log('send to top');
sendPendingToTop(id);
}
return (
<div className={QueueItemMain}>
<div>{id}</div>
<div className={[QueueItemMain, status].join(' ')}>
{/* @ts-expect-error */}
<div>{status}</div>
<div>{prompt}</div>
<div>{seed}</div>
<div>{sampler}</div>
<div className={QueueButtons}>
{status === QueueStatus.processing && (
<StopButton></StopButton>
)}
{status === QueueStatus.complete && (
<button className={CompleteButtton} onClick={removeFromQueue}>Clear</button>
)}
{status === QueueStatus.pending && (
<>
<button className={CancelButton} onClick={removeFromQueue}>Remove</button>
<button className={PauseButton} onClick={pauseItem}>Pause</button>
<button className={SendToTopButton} onClick={sendToTop}>Send to top</button>
</>
)}
{status === QueueStatus.paused && (
<button className={ResumeButton} onClick={retryRequest}>Resume</button>
)}
{status === QueueStatus.error && (
<button className={RetryButton} onClick={retryRequest}>Retry</button>
)}
</div>
</div>
);
}

View File

@ -1,9 +1,68 @@
import { style } from "@vanilla-extract/css";
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../../styles/theme/index.css";
import { BrandedButton } from "../../../../styles/shared.css";
import { QueueStatus } from "../../../../stores/requestQueueStore";
export const QueueItemMain = style({
display: "flex",
flexDirection: "column",
width: "100%",
padding: "0.5rem",
});
});
globalStyle(`${QueueItemMain}.${QueueStatus.processing}`, {
backgroundColor: vars.colors.warning,
});
globalStyle(`${QueueItemMain}.${QueueStatus.pending}`, {
backgroundColor: vars.colors.backgroundDark,
});
globalStyle(`${QueueItemMain}.${QueueStatus.paused}`, {
backgroundColor: vars.colors.backgroundAlt,
});
globalStyle(`${QueueItemMain}.${QueueStatus.complete}`, {
backgroundColor: vars.colors.success,
});
globalStyle(`${QueueItemMain}.${QueueStatus.error}`, {
backgroundColor: vars.colors.error,
});
export const QueueButtons = style({
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
});
// TODO these should be a button recipe?
export const CompleteButtton = style([BrandedButton, {
}]);
export const PauseButton = style([BrandedButton, {
}]);
export const ResumeButton = style([BrandedButton, {
}]);
export const CancelButton = style([BrandedButton, {
}]);
export const RetryButton = style([BrandedButton, {
}]);
export const SendToTopButton = style([BrandedButton, {
}]);

View File

@ -27,7 +27,6 @@ interface ImageFetchingState {
setStartTime: () => void;
setNowTime: () => void;
resetForFetching: () => void;
}
export const useImageFetching = create<ImageFetchingState>((set) => ({

View File

@ -1,78 +0,0 @@
import create from "zustand";
import produce from "immer";
import { useRandomSeed } from "../utils";
import { ImageRequest } from "../api";
interface QueueItem {
id?: string;
options?: ImageRequest;
status?: "pending" | "complete" | "error";
}
interface ImageQueueState {
images: QueueItem[];
completedImageIds: string[];
addNewImage: (id: string, imgRec: ImageRequest) => void;
hasQueuedImages: () => boolean;
firstInQueue: () => QueueItem;
removeFirstInQueue: () => void;
clearQueue: () => void;
clearCachedIds: () => void;
}
export const useImageQueue = create<ImageQueueState>((set, get) => ({
images: [],
completedImageIds: [],
// use produce to make sure we don't mutate state
addNewImage: (id: string, imgRec: ImageRequest) => {
set(
produce((state) => {
const item: QueueItem = { id, options: imgRec, status: "pending" };
state.images.push(item);
})
);
},
hasQueuedImages: () => {
return get().images.length > 0;
},
firstInQueue: () => {
const { images } = get();
if (images.length > 0) {
return images[0];
}
// // cast an empty object to QueueItem
const empty: QueueItem = {};
return empty;
},
removeFirstInQueue: () => {
set(
produce((state) => {
const image = state.images.shift();
if (void 0 !== image) {
state.completedImageIds.push(image.id);
}
})
);
},
clearQueue: () => {
set(
produce((state) => {
state.images = [];
})
);
},
clearCachedIds: () => {
set(
produce((state) => {
state.completedImageIds = [];
})
);
},
}));

View File

@ -0,0 +1,143 @@
import create from "zustand";
import produce from "immer";
import { ImageRequest } from "../api";
export enum QueueStatus {
pending = "pending",
processing = "processing",
complete = "complete",
paused = "paused",
error = "error",
}
export interface QueuedRequest {
id: string;
options: ImageRequest;
status: QueueStatus[keyof QueueStatus];
//"pending" | "processing" | "complete" | "error";
}
interface RequestQueueState {
requests: QueuedRequest[];
addtoQueue: (id: string, imgRec: ImageRequest) => void;
pendingRequests: () => QueuedRequest[];
hasPendingQueue: () => boolean;
hasAnyQueue: () => boolean;
firstInQueue: () => QueuedRequest;
updateStatus: (id: string, status: QueueStatus[keyof QueueStatus]) => void;
sendPendingToTop: (id: string) => void;
removeItem: (id: string) => void;
removeCompleted: () => void;
removeErrored: () => void;
clearQueue: () => void;
}
export const useRequestQueue = create<RequestQueueState>((set, get) => ({
requests: [],
// use produce to make sure we don't mutate state
addtoQueue: (id: string, imgRec: ImageRequest) => {
set(
produce((state) => {
const item: QueuedRequest = { id, options: imgRec, status: QueueStatus.pending };
state.requests.push(item);
})
);
},
pendingRequests: () => {
return get().requests.filter((item) => item.status === QueueStatus.pending);
},
hasPendingQueue: () => {
return get().pendingRequests().length > 0;
},
hasAnyQueue: () => {
return get().requests.length > 0;
},
firstInQueue: () => {
const pending = get().pendingRequests()[0];
if (pending === undefined) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const temp: QueuedRequest = { id: "", options: ({} as ImageRequest), status: QueueStatus.pending };
return temp;
}
return pending;
},
updateStatus: (id: string, status: QueueStatus[keyof QueueStatus]) => {
set(
produce((state) => {
const item = state.requests.find((item: QueuedRequest) => item.id === id);
if (void 0 !== item) {
item.status = status;
}
})
);
},
sendPendingToTop: (id: string) => {
set(
produce((state) => {
const item = state.requests.find((item: QueuedRequest) => item.id === id);
if (void 0 !== item) {
const index = state.requests.indexOf(item);
// insert infront of the pending requests
const pending = get().pendingRequests();
const pendingIndex = state.requests.indexOf(pending[0]);
console.log()
state.requests.splice(index, 1);
state.requests.splice(pendingIndex, 0, item);
}
})
);
},
removeItem: (id: string) => {
set(
produce((state) => {
const index = state.requests.findIndex((item: QueuedRequest) => item.id === id);
if (index > -1) {
state.requests.splice(index, 1);
}
})
);
},
removeCompleted: () => {
set(
produce((state) => {
const completed = state.requests.filter((item: QueuedRequest) => item.status === QueueStatus.complete);
completed.forEach((item: QueuedRequest) => {
const index = state.requests.indexOf(item);
state.requests.splice(index, 1);
});
})
);
},
removeErrored: () => {
set(
produce((state) => {
const errored = state.requests.filter((item: QueuedRequest) => item.status === QueueStatus.error);
errored.forEach((item: QueuedRequest) => {
const index = state.requests.indexOf(item);
state.requests.splice(index, 1);
});
})
);
},
clearQueue: () => {
set(
produce((state) => {
state.requests = [];
})
);
},
}));