Merge pull request #24 from caranicas/beta-react-display-loading

Beta react display loading
This commit is contained in:
caranicas 2022-09-18 15:44:31 -04:00 committed by GitHub
commit 51f1759323
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 265 additions and 184 deletions

View File

@ -21,6 +21,7 @@
"zustand": "^4.1.1" "zustand": "^4.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^18.7.18",
"@types/react": "^18.0.17", "@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
@ -613,6 +614,12 @@
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
} }
}, },
"node_modules/@types/node": {
"version": "18.7.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz",
"integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==",
"dev": true
},
"node_modules/@types/prop-types": { "node_modules/@types/prop-types": {
"version": "15.7.5", "version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@ -2361,6 +2368,12 @@
"use-sync-external-store": "^1.2.0" "use-sync-external-store": "^1.2.0"
} }
}, },
"@types/node": {
"version": "18.7.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz",
"integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==",
"dev": true
},
"@types/prop-types": { "@types/prop-types": {
"version": "15.7.5", "version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",

View File

@ -23,6 +23,7 @@
"zustand": "^4.1.1" "zustand": "^4.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^18.7.18",
"@types/react": "^18.0.17", "@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",

View File

@ -60,7 +60,6 @@ export default function DrawImage({
const _handleMouseDown = ( const _handleMouseDown = (
e: React.MouseEvent<HTMLCanvasElement, MouseEvent> e: React.MouseEvent<HTMLCanvasElement, MouseEvent>
) => { ) => {
const { const {
nativeEvent: { offsetX, offsetY }, nativeEvent: { offsetX, offsetY },
} = e; } = e;

View File

@ -1,34 +1,20 @@
import { style } from "@vanilla-extract/css"; import { style } from "@vanilla-extract/css";
export const generatedImage = style({ export const generatedImageMain = style({
position: "relative", position: "relative",
width: "512px",
height: "512px",
}); });
export const imageContain = style({ // export const imageContain = style({
width: "512px", // width: "512px",
height: "512px", // height: "512px",
backgroundColor: "black", // backgroundColor: "black",
display: "flex", // display: "flex",
justifyContent: "center", // justifyContent: "center",
alignItems: "center", // alignItems: "center",
}); // });
export const image = style({ export const image = style({
width: "512px", // width: "512px",
height: "512px", // height: "512px",
objectFit: "contain", // objectFit: "contain",
});
export const saveButton = style({
position: "absolute",
bottom: "10px",
left: "10px",
});
export const useButton = style({
position: "absolute",
bottom: "10px",
right: "10px",
}); });

View File

@ -3,17 +3,15 @@ import React, { useCallback } from "react";
import { ImageRequest, useImageCreate } from "../../../stores/imageCreateStore"; import { ImageRequest, useImageCreate } from "../../../stores/imageCreateStore";
import { import {
generatedImage, generatedImageMain,
imageContain, image, //@ts-ignore
image,
saveButton,
useButton, //@ts-ignore
} from "./generatedImage.css.ts"; } from "./generatedImage.css.ts";
type GeneretaedImageProps = { type GeneretaedImageProps = {
imageData: string; imageData: string;
metadata: ImageRequest; metadata: ImageRequest;
className?: string; className?: string;
// children: never[];
}; };
export default function GeneratedImage({ export default function GeneratedImage({
@ -21,65 +19,9 @@ export default function GeneratedImage({
metadata, metadata,
className, className,
}: GeneretaedImageProps) { }: GeneretaedImageProps) {
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const createFileName = () => {
const {
prompt,
seed,
num_inference_steps,
guidance_scale,
use_face_correction,
use_upscale,
width,
height,
} = metadata;
//Most important information is the prompt
let underscoreName = prompt.replace(/[^a-zA-Z0-9]/g, "_");
underscoreName = underscoreName.substring(0, 100);
// name and the top level metadata
let fileName = `${underscoreName}_Seed-${seed}_Steps-${num_inference_steps}_Guidance-${guidance_scale}`;
// Add the face correction and upscale
if (use_face_correction) {
fileName += `_FaceCorrection-${use_face_correction}`;
}
if (use_upscale) {
fileName += `_Upscale-${use_upscale}`;
}
// Add the width and height
fileName += `_${width}x${height}`;
// add the file extension
fileName += `.png`;
// return fileName
return fileName;
};
const _handleSave = () => {
const link = document.createElement("a");
link.download = createFileName();
link.href = imageData;
link.click();
};
const _handleUseAsInput = () => {
setRequestOption("init_image", imageData);
};
// className={[statusClass, className].join(" ")}
return ( return (
<div className={[generatedImage, className].join(" ")}> <div className={[generatedImageMain, className].join(" ")}>
<p>{metadata.prompt}</p> <img className={image} src={imageData} alt={metadata.prompt} />
<div className={imageContain}>
<img className={image} src={imageData} alt="generated" />
<button className={saveButton} onClick={_handleSave}>
Save
</button>
<button className={useButton} onClick={_handleUseAsInput}>
Use as Input
</button>
</div>
</div> </div>
); );
} }

View File

@ -4,6 +4,9 @@ import AdvancedSettings from "./advancedSettings";
import ImageModifiers from "./imageModifiers"; import ImageModifiers from "./imageModifiers";
import InpaintingPanel from "./inpaintingPanel"; import InpaintingPanel from "./inpaintingPanel";
// this works but causes type errors so its not worth it for now
// import { useImageCreate } from "@stores/imageCreateStore.ts";
import { useImageCreate } from "../../../stores/imageCreateStore"; import { useImageCreate } from "../../../stores/imageCreateStore";
import "./creationPanel.css"; import "./creationPanel.css";

View File

@ -0,0 +1,38 @@
import { style, globalStyle } from "@vanilla-extract/css";
// @ts-ignore
import { vars } from "../../../../styles/theme/index.css.ts";
export const completedImagesMain = style({
display: "flex",
flexDirection: "row",
flexWrap: "nowrap",
height: "100%",
width: "100%",
overflow: "auto",
paddingBottom: vars.spacing.medium,
});
export const imageContain = style({
width: "112px",
backgroundColor: "black",
display: "flex",
justifyContent: "center",
alignItems: "center",
flexShrink: 0,
border: "0 none",
padding: "0",
});
globalStyle(`${imageContain} img`, {
width: "100%",
objectFit: "contain",
});
globalStyle(`${completedImagesMain} > ${imageContain}:first-of-type`, {
marginLeft: vars.spacing.medium,
});
globalStyle(`${imageContain} > ${imageContain}:last-of-type`, {
marginRight: 0,
});

View File

@ -1,18 +1,58 @@
export const CompletedImages = () => { import React from "react";
import { CompletedImagesType } from "../index";
type CurrentDisplayProps = {
images: CompletedImagesType[] | null;
setCurrentDisplay: (image: CompletedImagesType) => void;
};
import {
completedImagesMain,
imageContain, //@ts-ignore
} from "./completedImages.css.ts";
export default function CompletedImages({
images,
setCurrentDisplay,
}: CurrentDisplayProps) {
const _handleSetCurrentDisplay = (index: number) => {
debugger;
const image = images![index];
setCurrentDisplay(image);
};
console.log("COMP{LETED IMAGES", images);
return ( return (
<div className="completed-images"> <div className={completedImagesMain}>
<h1>Completed Images</h1> {images &&
images.map((image, index) => {
// if (void 0 !== image) {
// return null;
// }
return (
// <div className={imageContain} key={index} value={index} onClick={() => {
// debugger;
// const image = images[index];
// _handleSetCurrentDisplay(image);
// }}>
// <img src={image.data} alt={image.info.prompt} />
// </div>
<button
key={index}
className={imageContain}
onClick={() => {
console.log("CLICKED", index);
debugger;
_handleSetCurrentDisplay(index);
}}
>
<img src={image.data} alt={image.info.prompt} />
</button>
);
})}
</div> </div>
); );
// const { data } = useQuery("completedImages", getCompletedImages); }
// return (
// <div className="completed-images">
// <h2>Completed Images</h2>
// <div className="completed-images-list">
// {data?.map((image) => (
// <GeneratedImage imageData={image.data} key={image.id} />
// ))}
// </div>
// </div>
// );
};

View File

@ -0,0 +1,78 @@
import React from "react";
import GeneratedImage from "../../../molecules/generatedImage";
import {
ImageRequest,
useImageCreate,
} from "../../../../stores/imageCreateStore";
import { CompletedImagesType } from "../index";
type CurrentDisplayProps = {
image: CompletedImagesType | null;
};
export default function CurrentDisplay({ image }: CurrentDisplayProps) {
const { info, data } = image || { info: null, data: null };
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const createFileName = () => {
const {
prompt,
seed,
num_inference_steps,
guidance_scale,
use_face_correction,
use_upscale,
width,
height,
} = info!;
//Most important information is the prompt
let underscoreName = prompt.replace(/[^a-zA-Z0-9]/g, "_");
underscoreName = underscoreName.substring(0, 100);
// name and the top level metadata
let fileName = `${underscoreName}_Seed-${seed}_Steps-${num_inference_steps}_Guidance-${guidance_scale}`;
// Add the face correction and upscale
if (use_face_correction) {
fileName += `_FaceCorrection-${use_face_correction}`;
}
if (use_upscale) {
fileName += `_Upscale-${use_upscale}`;
}
// Add the width and height
fileName += `_${width}x${height}`;
// add the file extension
fileName += `.png`;
// return fileName
return fileName;
};
const _handleSave = () => {
const link = document.createElement("a");
link.download = createFileName();
link.href = data!;
link.click();
};
const _handleUseAsInput = () => {
setRequestOption("init_image", data);
};
return (
<div className="current-display">
{image && (
<div>
<p> {info!.prompt}</p>
<GeneratedImage imageData={data!} metadata={info!}></GeneratedImage>
<div>
<button onClick={_handleSave}>Save</button>
<button onClick={_handleUseAsInput}>Use as Input</button>
</div>
</div>
)}
<div></div>
</div>
);
}

View File

@ -4,29 +4,19 @@ import { style } from "@vanilla-extract/css";
import { vars } from "../../../styles/theme/index.css.ts"; import { vars } from "../../../styles/theme/index.css.ts";
export const displayPanel = style({ export const displayPanel = style({
padding: vars.spacing.medium, height: "100%",
display: "flex",
flexDirection: "column",
}); });
export const displayContainer = style({ export const displayContainer = style({
flexGrow: 1,
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "column",
height: "100%", justifyContent: "center",
width: "100%", alignItems: "center",
overflow: "hidden",
});
export const CurrentDisplay = style({
width: "512px",
height: "100%",
}); });
export const previousImages = style({ export const previousImages = style({
marginLeft: vars.spacing.large, height: "150px",
display: "flex",
flex: "auto",
flexWrap: "wrap",
});
export const previousImage = style({
margin: `0 ${vars.spacing.small}`,
}); });

View File

@ -9,18 +9,21 @@ import { doMakeImage, MakeImageKey } from "../../../api";
import AudioDing from "./audioDing"; import AudioDing from "./audioDing";
import GeneratedImage from "../../molecules/generatedImage"; // import GeneratedImage from "../../molecules/generatedImage";
// import DrawImage from "../../molecules/drawImage"; // import DrawImage from "../../molecules/drawImage";
import CurrentDisplay from "./currentDisplay";
import CompletedImages from "./completedImages";
import { import {
displayPanel, displayPanel,
displayContainer, displayContainer,
CurrentDisplay, // CurrentDisplay,
previousImages, previousImages,
previousImage, //@ts-ignore previousImage, //@ts-ignore
} from "./displayPanel.css.ts"; } from "./displayPanel.css.ts";
type CompletedImagesType = { export type CompletedImagesType = {
id: string; id: string;
data: string; data: string;
info: ImageRequest; info: ImageRequest;
@ -29,13 +32,13 @@ type CompletedImagesType = {
export default function DisplayPanel() { export default function DisplayPanel() {
const dingRef = useRef<HTMLAudioElement>(null); const dingRef = useRef<HTMLAudioElement>(null);
const isSoundEnabled = useImageCreate((state) => state.isSoundEnabled()); const isSoundEnabled = useImageCreate((state) => state.isSoundEnabled());
const isInPaintingMode = useImageCreate((state) => state.isInpainting);
/* FETCHING */
// @ts-ignore // @ts-ignore
const { id, options } = useImageQueue((state) => state.firstInQueue()); const { id, options } = useImageQueue((state) => state.firstInQueue());
const removeFirstInQueue = useImageQueue((state) => state.removeFirstInQueue); const removeFirstInQueue = useImageQueue((state) => state.removeFirstInQueue);
const [currentImage, setCurrentImage] = useState<CompletedImagesType | null>(
null
);
const { status, data } = useQuery( const { status, data } = useQuery(
[MakeImageKey, id], [MakeImageKey, id],
() => doMakeImage(options), () => doMakeImage(options),
@ -58,16 +61,15 @@ export default function DisplayPanel() {
}, [status, data, removeFirstInQueue, dingRef, isSoundEnabled]); }, [status, data, removeFirstInQueue, dingRef, isSoundEnabled]);
/* COMPLETED IMAGES */ /* COMPLETED IMAGES */
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [completedImages, setCompletedImages] = useState<CompletedImagesType[]>( const [completedImages, setCompletedImages] = useState<CompletedImagesType[]>(
[] []
); );
const completedIds = useImageQueue((state) => state.completedImageIds); const completedIds = useImageQueue((state) => state.completedImageIds);
const init_image = useImageCreate((state) => // const init_image = useImageCreate((state) =>
state.getValueForRequestKey("init_image") // state.getValueForRequestKey("init_image")
); // );
useEffect(() => { useEffect(() => {
const testReq = {} as ImageRequest; const testReq = {} as ImageRequest;
@ -97,50 +99,24 @@ export default function DisplayPanel() {
.flat() .flat()
.reverse(); .reverse();
setCompletedImages(temp); setCompletedImages(temp);
setCurrentImage(temp[0] || null);
} else { } else {
setCompletedImages([]); setCompletedImages([]);
setCurrentImage(null);
} }
}, [setCompletedImages, queryClient, completedIds]); }, [setCompletedImages, setCurrentImage, queryClient, completedIds]);
return ( return (
<div className={displayPanel}> <div className={displayPanel}>
<AudioDing ref={dingRef}></AudioDing> <AudioDing ref={dingRef}></AudioDing>
<div className={displayContainer}> <div className={displayContainer}>
{/* {isInPaintingMode && <DrawImage imageData={init_image}></DrawImage>} */} <CurrentDisplay image={currentImage}></CurrentDisplay>
</div>
{completedImages.length > 0 && ( <div className={previousImages}>
<> <CompletedImages
<div className={CurrentDisplay}> images={completedImages}
<GeneratedImage setCurrentDisplay={setCurrentImage}
key={completedImages[0].id} ></CompletedImages>
imageData={completedImages[0].data}
metadata={completedImages[0].info}
/>
</div>
<div className={previousImages}>
{completedImages.map((image, index) => {
if (void 0 !== image) {
if (index == 0) {
return null;
}
return (
<GeneratedImage
className={previousImage}
key={image.id}
imageData={image.data}
metadata={image.info}
/>
);
} else {
console.warn("image is undefined", image, index);
return null;
}
})}
</div>
</>
)}
</div> </div>
</div> </div>
); );

View File

@ -1,5 +1,10 @@
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": "./",
"paths": {
"@stores": ["src/stores"]
},
"target": "ESNext", "target": "ESNext",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"], "lib": ["DOM", "DOM.Iterable", "ESNext"],

View File

@ -2,14 +2,24 @@ import { defineConfig } from "vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin"; import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
import path from "path";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
resolve: {
alias: {
// TODO figure out why vs code complains about this even though it works
"@stores": path.resolve(__dirname, "./src/stores"),
// TODO - add more aliases
},
},
plugins: [ plugins: [
react(), react(),
vanillaExtractPlugin({ vanillaExtractPlugin({
// configuration // configuration
}), }),
], ],
server: { server: {
port: 9001, port: 9001,
}, },

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long