mirror of
https://github.com/easydiffusion/easydiffusion.git
synced 2024-11-07 17:04:01 +01:00
inital push
This commit is contained in:
parent
1d88a5b42e
commit
34aa60d47b
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,4 +1,7 @@
|
||||
__pycache__
|
||||
installer
|
||||
installer.tar
|
||||
dist
|
||||
dist
|
||||
|
||||
# built code for the front end
|
||||
!/ui/frontend/dist
|
18
ui/frontend/build_src/.gitignore
vendored
Normal file
18
ui/frontend/build_src/.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# local ignores - We could move these to the global ignores,
|
||||
# but I think it makes sense to keep them here
|
||||
|
||||
# env
|
||||
*.local
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# installed dependencies
|
||||
node_modules
|
||||
|
1267
ui/frontend/build_src/index.html
Normal file
1267
ui/frontend/build_src/index.html
Normal file
File diff suppressed because it is too large
Load Diff
2571
ui/frontend/build_src/package-lock.json
generated
Normal file
2571
ui/frontend/build_src/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
ui/frontend/build_src/package.json
Normal file
28
ui/frontend/build_src/package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "react-ts",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build --emptyOutDir",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^4.2.3",
|
||||
"@tanstack/react-query-devtools": "^4.2.3",
|
||||
"immer": "^9.0.15",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"uuid": "^9.0.0",
|
||||
"zustand": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@vitejs/plugin-react": "^2.0.1",
|
||||
"typescript": "^4.6.4",
|
||||
"vite": "^3.0.7"
|
||||
}
|
||||
}
|
BIN
ui/frontend/build_src/public/ding.mp3
Normal file
BIN
ui/frontend/build_src/public/ding.mp3
Normal file
Binary file not shown.
BIN
ui/frontend/build_src/public/kofi.png
Normal file
BIN
ui/frontend/build_src/public/kofi.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
151
ui/frontend/build_src/public/modifiers.json
Normal file
151
ui/frontend/build_src/public/modifiers.json
Normal file
@ -0,0 +1,151 @@
|
||||
[
|
||||
[
|
||||
"Drawing Style",
|
||||
[
|
||||
"Cel Shading",
|
||||
"Children's Drawing",
|
||||
"Crosshatch",
|
||||
"Detailed and Intricate",
|
||||
"Doodle",
|
||||
"Dot Art",
|
||||
"Line Art",
|
||||
"Sketch"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Visual Style",
|
||||
[
|
||||
"2D",
|
||||
"8-bit",
|
||||
"16-bit",
|
||||
"Anaglyph",
|
||||
"Anime",
|
||||
"CGI",
|
||||
"Cartoon",
|
||||
"Comic Book",
|
||||
"Concept Art",
|
||||
"Digital Art",
|
||||
"Fantasy",
|
||||
"Graphic Novel",
|
||||
"Hard Edge Painting",
|
||||
"Hydrodipped",
|
||||
"Lithography",
|
||||
"Manga",
|
||||
"Modern Art",
|
||||
"Mosaic",
|
||||
"Mural",
|
||||
"Photo",
|
||||
"Realistic",
|
||||
"Street Art",
|
||||
"Visual Novel",
|
||||
"Watercolor"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Pen",
|
||||
[
|
||||
"Chalk",
|
||||
"Colored Pencil",
|
||||
"Graphite",
|
||||
"Ink",
|
||||
"Oil Paint",
|
||||
"Pastel Art"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Carving and Etching",
|
||||
[
|
||||
"Etching",
|
||||
"Linocut",
|
||||
"Paper Model",
|
||||
"Paper-Mache",
|
||||
"Papercutting",
|
||||
"Pyrography",
|
||||
"Wood-Carving"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Camera",
|
||||
[
|
||||
"Aerial View",
|
||||
"Canon50",
|
||||
"Cinematic",
|
||||
"Close-up",
|
||||
"Color Grading",
|
||||
"Dramatic",
|
||||
"Film Grain",
|
||||
"Fisheye Lens",
|
||||
"Glamor Shot",
|
||||
"Golden Hour",
|
||||
"HD",
|
||||
"Lens Flare",
|
||||
"Macro",
|
||||
"Polaroid",
|
||||
"Vintage",
|
||||
"War Photography",
|
||||
"White Balance",
|
||||
"Wildlife Photography"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Color",
|
||||
[
|
||||
"Beautiful Lighting",
|
||||
"Colorful",
|
||||
"Dynamic Lighting",
|
||||
"Electric Colors",
|
||||
"Infrared",
|
||||
"Synthwave",
|
||||
"Warm Color Palette"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Emotions",
|
||||
[
|
||||
"Angry",
|
||||
"Disgusted",
|
||||
"Embarrassed",
|
||||
"Evil",
|
||||
"Excited",
|
||||
"Fear",
|
||||
"Happy",
|
||||
"Lonely",
|
||||
"Sad",
|
||||
"Surprised"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Style of an artist or community",
|
||||
[
|
||||
"Artstation",
|
||||
"by Agnes Lawrence Pelton",
|
||||
"by Akihito Yoshida",
|
||||
"by Andy Warhol",
|
||||
"by Artgerm",
|
||||
"by Asaf Hanuka",
|
||||
"by Aubrey Beardsley",
|
||||
"by Banksy",
|
||||
"by Ben Enwonwu",
|
||||
"by Caravaggio Michelangelo Merisi",
|
||||
"by David Mann",
|
||||
"by Frida Kahlo",
|
||||
"by H.R. Giger",
|
||||
"by Hayao Miyazaki",
|
||||
"by Ivan Shishkin",
|
||||
"by Johannes Vermeer",
|
||||
"by John William Waterhouse",
|
||||
"by Katsushika Hokusai",
|
||||
"by Ko Young Hoon",
|
||||
"by Leonardo da Vinci",
|
||||
"by Lisa Frank",
|
||||
"by Mahmoud Saïd",
|
||||
"by Mark Brooks",
|
||||
"by Pablo Picasso",
|
||||
"by Richard Dadd",
|
||||
"by Salvador Dalí",
|
||||
"by Tivadar Csontváry Kosztka",
|
||||
"by Yoshitaka Amano",
|
||||
"by wlop"
|
||||
]
|
||||
]
|
||||
]
|
60
ui/frontend/build_src/src/App.css
Normal file
60
ui/frontend/build_src/src/App.css
Normal file
@ -0,0 +1,60 @@
|
||||
.App {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: auto;
|
||||
display: grid;
|
||||
background-color: rgb(32, 33, 36);
|
||||
grid-template-columns: 360px 1fr;
|
||||
grid-template-rows: 100px 1fr 300px;
|
||||
grid-template-areas: "header header header"
|
||||
"create display display"
|
||||
"footer footer footer";
|
||||
}
|
||||
|
||||
/* Very basic mobile stacked UI*/
|
||||
@media screen and (max-width: 768px) {
|
||||
.App {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 100px 1fr 1fr 300px;
|
||||
grid-template-areas: "header"
|
||||
"create"
|
||||
"display"
|
||||
"footer";
|
||||
}
|
||||
}
|
||||
|
||||
.header-layout {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
.create-layout {
|
||||
grid-area: create;
|
||||
}
|
||||
|
||||
.display-layout {
|
||||
grid-area: display;
|
||||
}
|
||||
|
||||
.footer-layout {
|
||||
grid-area: footer;
|
||||
|
||||
}
|
||||
|
||||
/* Copypasta from Bootstrap, makes content visually hidden but still accessible for screenreaders */
|
||||
.visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
padding: 0 !important;
|
||||
margin: -1px !important;
|
||||
overflow: hidden !important;
|
||||
clip: rect(0, 0, 0, 0) !important;
|
||||
white-space: nowrap !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
/* TODO proper utility classes */
|
||||
.mb-4 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
46
ui/frontend/build_src/src/App.tsx
Normal file
46
ui/frontend/build_src/src/App.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import './App.css'
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { getSaveDirectory } from './api'
|
||||
import { useImageCreate } from "./store/imageCreateStore";
|
||||
|
||||
// Todo - import components here
|
||||
import HeaderDisplay from './components/headerDisplay';
|
||||
import CreationPanel from './components/creationPanel';
|
||||
import DisplayPanel from './components/displayPanel';
|
||||
import FooterDisplay from './components/footerDisplay';
|
||||
|
||||
function App() {
|
||||
|
||||
// Get the original save directory
|
||||
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
|
||||
const { status, data } = useQuery(
|
||||
['SaveDir'], getSaveDirectory,
|
||||
);
|
||||
useEffect(() => {
|
||||
if(status === 'success') {
|
||||
setRequestOption("save_to_disk_path", data);
|
||||
}
|
||||
}, [setRequestOption, status, data]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="header-layout">
|
||||
<HeaderDisplay></HeaderDisplay>
|
||||
</header>
|
||||
<nav className="create-layout">
|
||||
<CreationPanel></CreationPanel>
|
||||
</nav>
|
||||
<main className="display-layout">
|
||||
<DisplayPanel></DisplayPanel>
|
||||
</main>
|
||||
<footer className="footer-layout">
|
||||
<FooterDisplay></FooterDisplay>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
55
ui/frontend/build_src/src/api/index.ts
Normal file
55
ui/frontend/build_src/src/api/index.ts
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* basic server health
|
||||
*/
|
||||
|
||||
import type {ImageRequest} from '../store/imageCreateStore';
|
||||
|
||||
// when we are on dev we want to specifiy 9000 as the port for the backend
|
||||
// when we are on prod we want be realtive to the current url
|
||||
const API_URL = import.meta.env.DEV ? 'http://localhost:9000' : '';
|
||||
|
||||
|
||||
export const HEALTH_PING_INTERVAL = 5000; // 5 seconds
|
||||
export const healthPing = async () => {
|
||||
|
||||
const pingUrl = `${API_URL}/ping`;
|
||||
let response = await fetch(pingUrl)
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* the local list of modifications
|
||||
*/
|
||||
export const loadModifications = async () => {
|
||||
const response = await fetch(`${API_URL}/modifiers.json`);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
export const getSaveDirectory = async () => {
|
||||
const response = await fetch(`${API_URL}/output_dir`);
|
||||
const data = await response.json();
|
||||
return data[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* post a new request for an image
|
||||
*/
|
||||
|
||||
export const MakeImageKey = 'MakeImage';
|
||||
export const doMakeImage = async (reqBody: ImageRequest) => {
|
||||
|
||||
const res = await fetch(`${API_URL}/image`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(reqBody)
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
input[size="4"] {
|
||||
width: 4.5rem;
|
||||
}
|
@ -0,0 +1,353 @@
|
||||
import React, {useEffect} from "react";
|
||||
import { useImageCreate } from "../../../store/imageCreateStore";
|
||||
import "./advancedSettings.css";
|
||||
|
||||
// todo: move this someplace more global
|
||||
const IMAGE_DIMENSIONS = [
|
||||
{ value: 128, label: "128 (*)" },
|
||||
{ value: 192, label: "192" },
|
||||
{ value: 256, label: "256 (*)" },
|
||||
{ value: 320, label: "320" },
|
||||
{ value: 384, label: "384" },
|
||||
{ value: 448, label: "448" },
|
||||
{ value: 512, label: "512 (*)" },
|
||||
{ value: 576, label: "576" },
|
||||
{ value: 640, label: "640" },
|
||||
{ value: 704, label: "704" },
|
||||
{ value: 768, label: "768 (*)" },
|
||||
{ value: 832, label: "832" },
|
||||
{ value: 896, label: "896" },
|
||||
{ value: 960, label: "960" },
|
||||
{ value: 1024, label: "1024 (*)" },
|
||||
];
|
||||
|
||||
function SettingsList() {
|
||||
|
||||
const requestCount = useImageCreate((state) => state.requestCount);
|
||||
const setRequestCount = useImageCreate((state) => state.setRequestCount);
|
||||
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
|
||||
|
||||
|
||||
const toggleUseFaceCorrection = useImageCreate((state) => state.toggleUseFaceCorrection);
|
||||
const isUsingFaceCorrection = useImageCreate((state) => state.isUsingFaceCorrection());
|
||||
|
||||
const toggleUseUpscaling = useImageCreate((state) => state.toggleUseUpscaling);
|
||||
const isUsingUpscaling = useImageCreate((state) => state.isUsingUpscaling());
|
||||
|
||||
|
||||
const toggleUseRandomSeed = useImageCreate((state) => state.toggleUseRandomSeed);
|
||||
const isRandomSeed = useImageCreate((state) => state.isRandomSeed());
|
||||
|
||||
const toggleUseAutoSave = useImageCreate((state) => state.toggleUseAutoSave);
|
||||
const isUseAutoSave = useImageCreate((state) => state.isUseAutoSave());
|
||||
|
||||
const toggleSoundEnabled = useImageCreate((state) => state.toggleSoundEnabled);
|
||||
const isSoundEnabled = useImageCreate((state) => state.isSoundEnabled());
|
||||
|
||||
const use_upscale = useImageCreate((state) => state.getValueForRequestKey(('use_upscale')));
|
||||
const show_only_filtered_image = useImageCreate((state) => state.getValueForRequestKey(('show_only_filtered_image')));
|
||||
const seed = useImageCreate((state) => state.getValueForRequestKey(('seed')));
|
||||
const width = useImageCreate((state) => state.getValueForRequestKey(('width')));
|
||||
const num_outputs = useImageCreate((state) => state.getValueForRequestKey(('num_outputs')));
|
||||
const height = useImageCreate((state) => state.getValueForRequestKey(('height')));
|
||||
const steps = useImageCreate((state) => state.getValueForRequestKey(('num_inference_steps')));
|
||||
const guidance_scale = useImageCreate((state) => state.getValueForRequestKey(('guidance_scale')));
|
||||
const prompt_strength = useImageCreate((state) => state.getValueForRequestKey(('prompt_strength')));
|
||||
const save_to_disk_path = useImageCreate((state) => state.getValueForRequestKey(('save_to_disk_path')));
|
||||
const turbo = useImageCreate((state) => state.getValueForRequestKey(('turbo')));
|
||||
const use_cpu = useImageCreate((state) => state.getValueForRequestKey(('use_cpu')));
|
||||
const use_full_precision = useImageCreate((state) => state.getValueForRequestKey(('use_full_precision')));
|
||||
|
||||
return (
|
||||
<ul id="editor-settings-entries">
|
||||
|
||||
{/*IMAGE CORRECTION */}
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isUsingFaceCorrection}
|
||||
onChange={(e) =>
|
||||
toggleUseFaceCorrection()
|
||||
}
|
||||
/>
|
||||
Fix incorrect faces and eyes (uses GFPGAN)
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isUsingUpscaling}
|
||||
onChange={(e) =>
|
||||
toggleUseUpscaling()
|
||||
}
|
||||
/>
|
||||
Upscale the image to 4x resolution using
|
||||
<select id="upscale_model" name="upscale_model" disabled={!isUsingUpscaling} value={use_upscale}>
|
||||
<option value="RealESRGAN_x4plus">RealESRGAN_x4plus</option>
|
||||
<option value="RealESRGAN_x4plus_anime_6B">RealESRGAN_x4plus_anime_6B</option>
|
||||
</select>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={show_only_filtered_image}
|
||||
onChange={(e) =>
|
||||
setRequestOption("show_only_filtered_image", e.target.checked)
|
||||
}
|
||||
/>
|
||||
Show only filtered image
|
||||
|
||||
</label>
|
||||
</li>
|
||||
{/* SEED */}
|
||||
<li>
|
||||
<label>
|
||||
Seed:
|
||||
<input
|
||||
size={10}
|
||||
value={seed}
|
||||
onChange={(e) =>
|
||||
setRequestOption("seed", e.target.value)
|
||||
}
|
||||
disabled={isRandomSeed}
|
||||
placeholder="random"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isRandomSeed}
|
||||
onChange={(e) =>
|
||||
toggleUseRandomSeed()
|
||||
}
|
||||
/>{" "}
|
||||
Random Image
|
||||
</label>
|
||||
</li>
|
||||
{/* COUNT */}
|
||||
<li>
|
||||
<label>
|
||||
Number of images to make:{" "}
|
||||
<input
|
||||
type="number"
|
||||
value={requestCount}
|
||||
onChange={(e) =>
|
||||
setRequestCount(parseInt(e.target.value, 10))
|
||||
}
|
||||
size={4}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Generate in parallel:
|
||||
<input
|
||||
type="number"
|
||||
value={num_outputs}
|
||||
onChange={(e) =>
|
||||
setRequestOption("num_outputs", parseInt(e.target.value, 10))
|
||||
}
|
||||
size={4}
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
{/* DIMENTIONS */}
|
||||
<li>
|
||||
<label>
|
||||
Width:
|
||||
<select
|
||||
value={width}
|
||||
onChange={(e) =>
|
||||
setRequestOption("width", e.target.value)
|
||||
}
|
||||
>
|
||||
{IMAGE_DIMENSIONS.map((dimension) => (
|
||||
<option
|
||||
key={"width-option_" + dimension.value}
|
||||
value={dimension.value}
|
||||
>
|
||||
{dimension.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
Height:
|
||||
<select
|
||||
value={height}
|
||||
onChange={(e) =>
|
||||
setRequestOption("height", e.target.value)
|
||||
}
|
||||
>
|
||||
{IMAGE_DIMENSIONS.map((dimension) => (
|
||||
<option
|
||||
key={"height-option_" + dimension.value}
|
||||
value={dimension.value}
|
||||
>
|
||||
{dimension.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</li>
|
||||
{/* STEPS */}
|
||||
<li>
|
||||
<label>
|
||||
Number of inference steps:{" "}
|
||||
<input
|
||||
value={steps}
|
||||
onChange={(e) => {
|
||||
console.log('ON CHNAGE num_inference_steps', e.target.value)
|
||||
setRequestOption("num_inference_steps", e.target.value)
|
||||
}}
|
||||
size={4}
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
{/* GUIDANCE SCALE */}
|
||||
<li>
|
||||
<label>
|
||||
Guidance Scale:
|
||||
<input
|
||||
value={guidance_scale}
|
||||
onChange={(e) =>
|
||||
setRequestOption("guidance_scale", e.target.value)
|
||||
}
|
||||
type="range"
|
||||
min="0"
|
||||
max="20"
|
||||
step=".1"
|
||||
/>
|
||||
</label>
|
||||
<span>{guidance_scale}</span>
|
||||
</li>
|
||||
{/* PROMPT STRENGTH */}
|
||||
<li className="mb-4">
|
||||
<label>
|
||||
Prompt Strength:{" "}
|
||||
<input
|
||||
value={prompt_strength}
|
||||
onChange={(e) =>
|
||||
// setImageOptions({ promptStrength: Number(e.target.value) })
|
||||
setRequestOption("prompt_strength", e.target.value)
|
||||
}
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step=".05"
|
||||
/>
|
||||
</label>
|
||||
<span>{prompt_strength}</span>
|
||||
</li>
|
||||
{/* AUTO SAVE */}
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
checked={isUseAutoSave}
|
||||
onChange={(e) =>
|
||||
toggleUseAutoSave()
|
||||
}
|
||||
type="checkbox"
|
||||
/>
|
||||
Automatically save to{" "}
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
value={save_to_disk_path}
|
||||
onChange={(e) =>
|
||||
setRequestOption("save_to_disk_path", e.target.value)
|
||||
}
|
||||
size={40}
|
||||
disabled={!isUseAutoSave}
|
||||
/>
|
||||
<span className="visually-hidden">
|
||||
Path on disk where images will be saved
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
{/* SOUND */}
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
checked={isSoundEnabled}
|
||||
onChange={(e) =>
|
||||
toggleSoundEnabled()
|
||||
}
|
||||
type="checkbox"
|
||||
/>
|
||||
Play sound on task completion
|
||||
</label>
|
||||
</li>
|
||||
{/* GENERATE */}
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
checked={turbo}
|
||||
onChange={(e) =>
|
||||
setRequestOption("turbo", e.target.checked)
|
||||
}
|
||||
type="checkbox"
|
||||
/>
|
||||
Turbo mode (generates images faster, but uses an additional 1 GB
|
||||
of GPU memory)
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={use_cpu}
|
||||
onChange={(e) =>
|
||||
setRequestOption("use_cpu", e.target.checked)
|
||||
}
|
||||
/>
|
||||
Use CPU instead of GPU (warning: this will be *very* slow)
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
checked={use_full_precision}
|
||||
onChange={(e) =>
|
||||
setRequestOption("use_full_precision", e.target.checked)
|
||||
}
|
||||
type="checkbox"
|
||||
/>
|
||||
Use full precision (for GPU-only. warning: this will consume more
|
||||
VRAM)
|
||||
</label>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
// {/* <!-- <li><input id="allow_nsfw" name="allow_nsfw" type="checkbox"/> <label htmlFor="allow_nsfw">Allow NSFW Content (You confirm you are above 18 years of age)</label></li> --> */}
|
||||
|
||||
export default function AdvancedSettings() {
|
||||
|
||||
const advancedSettingsIsOpen = useImageCreate(
|
||||
(state) => state.uiOptions.advancedSettingsIsOpen
|
||||
);
|
||||
|
||||
const toggleAdvancedSettingsIsOpen = useImageCreate(
|
||||
(state) => state.toggleAdvancedSettingsIsOpen
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="panel-box">
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleAdvancedSettingsIsOpen}
|
||||
className="panel-box-toggle-btn"
|
||||
>
|
||||
<h4>Advanced Settings</h4>
|
||||
</button>
|
||||
{advancedSettingsIsOpen && <SettingsList/>}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
.panel-box-toggle-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
border: 0 none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selected-tags {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.selected-tags ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.modifier-list{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.modifierTag {
|
||||
display: inline-block;
|
||||
padding: 6px;
|
||||
background-color: rgb(38, 77, 141);
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.modifierTag.selected {
|
||||
background-color: rgb(131, 11, 121);
|
||||
}
|
||||
|
||||
.modifierTag p {
|
||||
margin: 0;
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { loadModifications } from "../../../api";
|
||||
|
||||
import { useImageCreate } from "../../../store/imageCreateStore";
|
||||
|
||||
import ModifierTag from "../modierTag";
|
||||
|
||||
|
||||
type ModifierListProps = {
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
function ModifierList({tags}: ModifierListProps) {
|
||||
const setImageOptions = useImageCreate((state) => state.setImageOptions);
|
||||
const imageOptions = useImageCreate((state) => state.imageOptions);
|
||||
return(
|
||||
<ul className="modifier-list">
|
||||
{tags.map((tag) => (
|
||||
<li key={tag} >
|
||||
<ModifierTag name={tag} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
type ModifierGroupingProps = {
|
||||
title: string;
|
||||
tags: string[];
|
||||
};
|
||||
|
||||
function ModifierGrouping({title, tags}: ModifierGroupingProps) {
|
||||
|
||||
// doing this localy for now, but could move to a store
|
||||
// and persist if we wanted to
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
// console.log('grouping', title, tags)
|
||||
|
||||
const _toggleExpand = () => {
|
||||
// console.log('toggle expand')
|
||||
setIsExpanded(!isExpanded);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="modifier-grouping">
|
||||
<div className="modifier-grouping-header" onClick={_toggleExpand}>
|
||||
<h5>{title}</h5>
|
||||
</div>
|
||||
{isExpanded && <ModifierList tags={tags} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ImageModifers() {
|
||||
const {status, data} = useQuery(["modifications"], loadModifications);
|
||||
|
||||
const imageModifierIsOpen = useImageCreate(
|
||||
(state) => state.uiOptions.imageModifierIsOpen
|
||||
);
|
||||
const toggleImageModifiersIsOpen = useImageCreate(
|
||||
(state) => state.toggleImageModifiersIsOpen
|
||||
);
|
||||
|
||||
// useEffect(() => {
|
||||
// console.log("imageModifers", status, data);
|
||||
// }, [status, data]);
|
||||
|
||||
|
||||
const handleClick = () => {
|
||||
// debugger;
|
||||
toggleImageModifiersIsOpen();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="panel-box">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClick}
|
||||
className="panel-box-toggle-btn"
|
||||
>
|
||||
{/* TODO: swap this manual collapse stuff out for some UI component? */}
|
||||
<h4>Image Modifiers (art styles, tags, ect)</h4>
|
||||
</button>
|
||||
|
||||
{/* @ts-ignore */}
|
||||
{imageModifierIsOpen && data.map((item, index) => {
|
||||
return (
|
||||
<ModifierGrouping key={item[0]} title={item[0]} tags={item[1]}/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
86
ui/frontend/build_src/src/components/creationPanel/index.tsx
Normal file
86
ui/frontend/build_src/src/components/creationPanel/index.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import React, { ChangeEvent } from "react";
|
||||
|
||||
import MakeButton from "./makeButton";
|
||||
import AdvancedSettings from "./advancedSettings";
|
||||
import ImageModifiers from "./imageModifiers";
|
||||
|
||||
import ModifierTag from "./modierTag";
|
||||
|
||||
import { useImageCreate } from "../../store/imageCreateStore";
|
||||
|
||||
import './creationPanel.css';
|
||||
|
||||
export default function CreationPanel() {
|
||||
|
||||
const promptText = useImageCreate((state) => state.getValueForRequestKey("prompt"));
|
||||
const init_image = useImageCreate((state) => state.getValueForRequestKey("init_image"));
|
||||
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
|
||||
const selectedtags = useImageCreate((state) => state.selectedTags());
|
||||
|
||||
const handlePromptChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setRequestOption("prompt", event.target.value);
|
||||
};
|
||||
|
||||
const _handleFileSelect = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
//console.log("file select", event);
|
||||
const file = event.target.files[0];
|
||||
|
||||
// console.log("file", file);
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
if (e.target) {
|
||||
debugger;
|
||||
setRequestOption("init_image", e.target.result);
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="create-panel">
|
||||
<div className="basic-create">
|
||||
<div className="prompt">
|
||||
<p>Prompt </p>
|
||||
<textarea value={promptText} onChange={handlePromptChange}></textarea>
|
||||
</div>
|
||||
|
||||
{/* <div className="seed-image">
|
||||
<p>Seed Image</p>
|
||||
<input type="file" accept="image/*" />
|
||||
</div> */}
|
||||
|
||||
|
||||
<div id="editor-inputs-init-image" className="row">
|
||||
<label ><b>Initial Image:</b> (optional) </label>
|
||||
<input id="init_image" name="init_image" type="file" onChange={_handleFileSelect}/><br/>
|
||||
<div id="init_image_preview" className="image_preview">
|
||||
{ init_image &&
|
||||
<img id="init_image_preview" src={init_image} width="100" height="100" />
|
||||
}
|
||||
<button id="init_image_clear" className="image_clear_btn">X</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MakeButton></MakeButton>
|
||||
<div className="selected-tags">
|
||||
<p>Active Tags</p>
|
||||
<ul>
|
||||
{selectedtags.map((tag) => (
|
||||
<li key={tag}>
|
||||
<ModifierTag name={tag}></ModifierTag>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="advanced-create">
|
||||
<AdvancedSettings></AdvancedSettings>
|
||||
<ImageModifiers></ImageModifiers>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import React, {useEffect, useState}from "react";
|
||||
|
||||
import { useImageCreate } from "../../../store/imageCreateStore";
|
||||
// import { useImageDisplay } from "../../../store/imageDisplayStore";
|
||||
import { useImageQueue } from "../../../store/imageQueueStore";
|
||||
// import { doMakeImage } from "../../../api";
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
export default function MakeButton() {
|
||||
|
||||
const builtRequest = useImageCreate((state) => state.builtRequest);
|
||||
const addNewImage = useImageQueue((state) => state.addNewImage);
|
||||
|
||||
const makeImage = () => {
|
||||
// todo turn this into a loop and adjust the parallel count
|
||||
//
|
||||
const req = builtRequest();
|
||||
addNewImage(uuidv4(), req)
|
||||
};
|
||||
|
||||
return (
|
||||
<button onClick={makeImage}>Make</button>
|
||||
);
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import React from "react";
|
||||
import { useImageCreate } from "../../../store/imageCreateStore";
|
||||
|
||||
|
||||
type ModifierTagProps = {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default function ModifierTag({name}: ModifierTagProps) {
|
||||
|
||||
const hasTag = useImageCreate((state) => state.hasTag(name)) ? "selected" : "";
|
||||
const toggleTag = useImageCreate((state) => state.toggleTag);
|
||||
|
||||
console.log('has tag', hasTag)
|
||||
|
||||
const _toggleTag = () => {
|
||||
toggleTag(name);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={"modifierTag " + hasTag} onClick={_toggleTag}>
|
||||
<p>{name}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
export const CompletedImages = () => {
|
||||
return (
|
||||
<div className="completed-images">
|
||||
<h1>Completed Images</h1>
|
||||
</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>
|
||||
// );
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useImageQueue } from "../../../store/imageQueueStore";
|
||||
|
||||
import { doMakeImage, MakeImageKey } from "../../../api";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import GeneratedImage from "../generatedImage";
|
||||
|
||||
|
||||
export default function CurrentImage() {
|
||||
|
||||
const [imageData, setImageData] = useState(null);
|
||||
// @ts-ignore
|
||||
const {id, options} = useImageQueue((state) => state.firstInQueue());
|
||||
console.log('CurrentImage id', id)
|
||||
|
||||
|
||||
const removeFirstInQueue = useImageQueue((state) => state.removeFirstInQueue);
|
||||
|
||||
const { status, data } = useQuery(
|
||||
[MakeImageKey, id],
|
||||
() => doMakeImage(options),
|
||||
{
|
||||
enabled: void 0 !== id,
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// query is done
|
||||
if(status === 'success') {
|
||||
console.log("success");
|
||||
|
||||
// check to make sure that the image was created
|
||||
if(data.status === 'succeeded') {
|
||||
console.log("succeeded");
|
||||
setImageData(data.output[0].data);
|
||||
removeFirstInQueue();
|
||||
}
|
||||
}
|
||||
|
||||
}, [status, data, removeFirstInQueue]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="current-display">
|
||||
<h1>Current Image</h1>
|
||||
{imageData && <GeneratedImage imageData={imageData} />}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
|
||||
|
||||
import { useImageCreate } from "../../../store/imageCreateStore";
|
||||
|
||||
export default function GeneratedImage({ imageData }: { imageData: string }) {
|
||||
|
||||
|
||||
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
|
||||
const _handleSave = () => {
|
||||
const link = document.createElement("a");
|
||||
link.download = "image.png";
|
||||
link.href = imageData;
|
||||
link.click();
|
||||
};
|
||||
|
||||
const _handleUseAsInput = () => {
|
||||
console.log(" TODO : use as input");
|
||||
|
||||
|
||||
setRequestOption("init_image", imageData);
|
||||
// initImageSelector.value = null
|
||||
// initImagePreview.src = imgBody
|
||||
|
||||
|
||||
// imgUseBtn.addEventListener('click', function() {
|
||||
// initImageSelector.value = null
|
||||
// initImagePreview.src = imgBody
|
||||
|
||||
// initImagePreviewContainer.style.display = 'block'
|
||||
// promptStrengthContainer.style.display = 'block'
|
||||
|
||||
// // maskSetting.style.display = 'block'
|
||||
|
||||
// randomSeedField.checked = false
|
||||
// seedField.value = seed
|
||||
// seedField.disabled = false
|
||||
// })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="generated-image">
|
||||
<p>Your image</p>
|
||||
<img src={imageData} alt="generated" />
|
||||
<button onClick={_handleSave}>
|
||||
Save
|
||||
</button>
|
||||
<button onClick={_handleUseAsInput}>
|
||||
Use as Input
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
78
ui/frontend/build_src/src/components/displayPanel/index.tsx
Normal file
78
ui/frontend/build_src/src/components/displayPanel/index.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import { useImageQueue } from "../../store/imageQueueStore";
|
||||
|
||||
import { ImageRequest } from '../../store/imageCreateStore';
|
||||
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
|
||||
|
||||
import { MakeImageKey } from "../../api";
|
||||
|
||||
import CurrentImage from "./currentImage";
|
||||
|
||||
import GeneratedImage from "./generatedImage";
|
||||
|
||||
|
||||
type CompletedImagesType = {
|
||||
id: string;
|
||||
data: string;
|
||||
}
|
||||
export default function DisplayPanel() {
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const [completedImages, setCompletedImages] = useState<CompletedImagesType[]>([]);
|
||||
const completedIds = useImageQueue((state) => state.completedImageIds);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const testReq = {} as ImageRequest;
|
||||
const completedQueries = completedIds.map((id) => {
|
||||
const imageData = queryClient.getQueryData([MakeImageKey,id])
|
||||
return imageData;
|
||||
});
|
||||
|
||||
console.log('completedQueries', completedQueries);
|
||||
|
||||
if (completedQueries.length > 0) {
|
||||
// map the completedImagesto a new array
|
||||
// and then set the state
|
||||
const temp = completedQueries.map((query, index ) => {
|
||||
// debugger;
|
||||
if(void 0 !== query) {
|
||||
return query.output.map((data)=>{
|
||||
return {id: `${completedIds[index]}-${data.seed}`, data: data.data}
|
||||
})
|
||||
}
|
||||
|
||||
}).flat().reverse();
|
||||
setCompletedImages(temp);
|
||||
}
|
||||
else {
|
||||
setCompletedImages([]);
|
||||
}
|
||||
|
||||
},[setCompletedImages, queryClient, completedIds]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="display-panel">
|
||||
<h1>Display Panel</h1>
|
||||
<div>
|
||||
<CurrentImage />
|
||||
{completedImages.map((image, index) => {
|
||||
if(index == 0){
|
||||
return null;
|
||||
}
|
||||
if(void 0 !== image) {
|
||||
return <GeneratedImage key={image.id} imageData={image.data} />;
|
||||
}
|
||||
else {
|
||||
console.warn('image is undefined', image, index);
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
.footer-display {
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
}
|
18
ui/frontend/build_src/src/components/footerDisplay/index.tsx
Normal file
18
ui/frontend/build_src/src/components/footerDisplay/index.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
import './footerDisplay.css';
|
||||
|
||||
export default function FooterDisplay() {
|
||||
return (
|
||||
<div id="footer" className="panel-box">
|
||||
<p>If you found this project useful and want to help keep it alive, please <a href="https://ko-fi.com/cmdr2_stablediffusion_ui" target="_blank"><img src="./kofi.png" id="coffeeButton"/></a> to help cover the cost of development and maintenance! Thank you for your support!</p>
|
||||
<p>Please feel free to join the <a href="https://discord.com/invite/u9yhsFmEkB" target="_blank">discord community</a> or <a href="https://github.com/cmdr2/stable-diffusion-ui/issues" target="_blank">file an issue</a> if you have any problems or suggestions in using this interface.</p>
|
||||
<div id="footer-legal">
|
||||
<p><b>Disclaimer:</b> The authors of this project are not responsible for any content generated using this interface.</p>
|
||||
<p>This license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, <br/>spread misinformation and target vulnerable groups. For the full list of restrictions please read <a href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/LICENSE" target="_blank">the license</a>.</p>
|
||||
<p>By using this software, you consent to the terms and conditions of the license.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
.header-display {
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.status-display {
|
||||
margin-left: 10px;
|
||||
}
|
14
ui/frontend/build_src/src/components/headerDisplay/index.tsx
Normal file
14
ui/frontend/build_src/src/components/headerDisplay/index.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
|
||||
import StatusDisplay from "./statusDisplay";
|
||||
|
||||
import './headerDisplay.css';
|
||||
|
||||
export default function HeaderDisplay() {
|
||||
return (
|
||||
<div className="header-display">
|
||||
<h1>Stable Diffusion UI v2.1.0</h1>
|
||||
<StatusDisplay className="status-display"></StatusDisplay>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,59 @@
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { healthPing, HEALTH_PING_INTERVAL } from '../../../api';
|
||||
|
||||
const startingMessage = 'Stable Diffusion is starting...';
|
||||
const successMessage = 'Stable Diffusion is ready to use!';
|
||||
const errorMessage = 'Stable Diffusion is not running!';
|
||||
|
||||
import './statusDisplay.css';
|
||||
|
||||
export default function StatusDisplay({className}: {className?: string}) {
|
||||
|
||||
const [statusMessage, setStatusMessage] = useState(startingMessage);
|
||||
const [statusClass, setStatusClass] = useState('starting');
|
||||
|
||||
|
||||
// doing this here for the time being, to show the data getting loaded
|
||||
// but this will be moved to the status display when it is created
|
||||
const {status, data} = useQuery(['health'], healthPing, {refetchInterval: HEALTH_PING_INTERVAL});
|
||||
useEffect(() => {
|
||||
console.log('health data', data);
|
||||
}, [data]);
|
||||
|
||||
|
||||
// const data = {};
|
||||
|
||||
useEffect(() => {
|
||||
console.log('status', status);
|
||||
|
||||
if (status === 'loading') {
|
||||
setStatusMessage(startingMessage);
|
||||
setStatusClass('starting');
|
||||
}
|
||||
else if (status === 'error') {
|
||||
setStatusMessage(errorMessage);
|
||||
setStatusClass('error');
|
||||
}
|
||||
|
||||
else if (status === 'success') {
|
||||
|
||||
if(data[0] === 'OK') {
|
||||
setStatusMessage(successMessage);
|
||||
setStatusClass('success');
|
||||
}
|
||||
else {
|
||||
setStatusMessage(errorMessage);
|
||||
setStatusClass('error');
|
||||
}
|
||||
}
|
||||
}, [status, data]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* alittle hacky but joins the class names, will probably need a better css in js solution or tailwinds*/}
|
||||
<p className={[statusClass, className].join(' ')}>{statusMessage}</p>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
.starting {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #d9534f;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #5cb85c;
|
||||
}
|
18
ui/frontend/build_src/src/index.css
Normal file
18
ui/frontend/build_src/src/index.css
Normal file
@ -0,0 +1,18 @@
|
||||
body {
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#root {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
/* pointer-events: none; */
|
||||
|
||||
/* this are used while we still have the original app code in the index.html */
|
||||
/* display: none; */
|
||||
z-index: 1;
|
||||
}
|
37
ui/frontend/build_src/src/main.tsx
Normal file
37
ui/frontend/build_src/src/main.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
|
||||
import {
|
||||
QueryClient,
|
||||
QueryClientProvider,
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||
|
||||
import { enableMapSet } from 'immer';
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
const queryClient = new QueryClient(
|
||||
{
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect : false,
|
||||
refetchOnMount : false,
|
||||
staleTime: Infinity,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
enableMapSet();
|
||||
// application entry point
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<App />
|
||||
<ReactQueryDevtools initialIsOpen={true} />
|
||||
</QueryClientProvider>
|
||||
</React.StrictMode>
|
||||
)
|
151
ui/frontend/build_src/src/modifiers.json
Normal file
151
ui/frontend/build_src/src/modifiers.json
Normal file
@ -0,0 +1,151 @@
|
||||
[
|
||||
[
|
||||
"Drawing Style",
|
||||
[
|
||||
"Cel Shading",
|
||||
"Children's Drawing",
|
||||
"Crosshatch",
|
||||
"Detailed and Intricate",
|
||||
"Doodle",
|
||||
"Dot Art",
|
||||
"Line Art",
|
||||
"Sketch"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Visual Style",
|
||||
[
|
||||
"2D",
|
||||
"8-bit",
|
||||
"16-bit",
|
||||
"Anaglyph",
|
||||
"Anime",
|
||||
"CGI",
|
||||
"Cartoon",
|
||||
"Comic Book",
|
||||
"Concept Art",
|
||||
"Digital Art",
|
||||
"Fantasy",
|
||||
"Graphic Novel",
|
||||
"Hard Edge Painting",
|
||||
"Hydrodipped",
|
||||
"Lithography",
|
||||
"Manga",
|
||||
"Modern Art",
|
||||
"Mosaic",
|
||||
"Mural",
|
||||
"Photo",
|
||||
"Realistic",
|
||||
"Street Art",
|
||||
"Visual Novel",
|
||||
"Watercolor"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Pen",
|
||||
[
|
||||
"Chalk",
|
||||
"Colored Pencil",
|
||||
"Graphite",
|
||||
"Ink",
|
||||
"Oil Paint",
|
||||
"Pastel Art"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Carving and Etching",
|
||||
[
|
||||
"Etching",
|
||||
"Linocut",
|
||||
"Paper Model",
|
||||
"Paper-Mache",
|
||||
"Papercutting",
|
||||
"Pyrography",
|
||||
"Wood-Carving"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Camera",
|
||||
[
|
||||
"Aerial View",
|
||||
"Canon50",
|
||||
"Cinematic",
|
||||
"Close-up",
|
||||
"Color Grading",
|
||||
"Dramatic",
|
||||
"Film Grain",
|
||||
"Fisheye Lens",
|
||||
"Glamor Shot",
|
||||
"Golden Hour",
|
||||
"HD",
|
||||
"Lens Flare",
|
||||
"Macro",
|
||||
"Polaroid",
|
||||
"Vintage",
|
||||
"War Photography",
|
||||
"White Balance",
|
||||
"Wildlife Photography"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Color",
|
||||
[
|
||||
"Beautiful Lighting",
|
||||
"Colorful",
|
||||
"Dynamic Lighting",
|
||||
"Electric Colors",
|
||||
"Infrared",
|
||||
"Synthwave",
|
||||
"Warm Color Palette"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Emotions",
|
||||
[
|
||||
"Angry",
|
||||
"Disgusted",
|
||||
"Embarrassed",
|
||||
"Evil",
|
||||
"Excited",
|
||||
"Fear",
|
||||
"Happy",
|
||||
"Lonely",
|
||||
"Sad",
|
||||
"Surprised"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Style of an artist or community",
|
||||
[
|
||||
"Artstation",
|
||||
"by Agnes Lawrence Pelton",
|
||||
"by Akihito Yoshida",
|
||||
"by Andy Warhol",
|
||||
"by Artgerm",
|
||||
"by Asaf Hanuka",
|
||||
"by Aubrey Beardsley",
|
||||
"by Banksy",
|
||||
"by Ben Enwonwu",
|
||||
"by Caravaggio Michelangelo Merisi",
|
||||
"by David Mann",
|
||||
"by Frida Kahlo",
|
||||
"by H.R. Giger",
|
||||
"by Hayao Miyazaki",
|
||||
"by Ivan Shishkin",
|
||||
"by Johannes Vermeer",
|
||||
"by John William Waterhouse",
|
||||
"by Katsushika Hokusai",
|
||||
"by Ko Young Hoon",
|
||||
"by Leonardo da Vinci",
|
||||
"by Lisa Frank",
|
||||
"by Mahmoud Saïd",
|
||||
"by Mark Brooks",
|
||||
"by Pablo Picasso",
|
||||
"by Richard Dadd",
|
||||
"by Salvador Dalí",
|
||||
"by Tivadar Csontváry Kosztka",
|
||||
"by Yoshitaka Amano",
|
||||
"by wlop"
|
||||
]
|
||||
]
|
||||
]
|
258
ui/frontend/build_src/src/store/imageCreateStore.ts
Normal file
258
ui/frontend/build_src/src/store/imageCreateStore.ts
Normal file
@ -0,0 +1,258 @@
|
||||
import create from 'zustand';
|
||||
import produce from 'immer';
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
import { useRandomSeed } from '../utils';
|
||||
|
||||
export type ImageCreationUiOptions = {
|
||||
advancedSettingsIsOpen: boolean;
|
||||
imageModifierIsOpen: boolean;
|
||||
isCheckedUseUpscaling: boolean;
|
||||
isCheckUseFaceCorrection: boolean;
|
||||
isUseRandomSeed: boolean;
|
||||
isUseAutoSave: boolean;
|
||||
isSoundEnabled: boolean;
|
||||
}
|
||||
|
||||
|
||||
export type ImageRequest = {
|
||||
prompt: string;
|
||||
seed: number;
|
||||
num_outputs: number;
|
||||
num_inference_steps: number;
|
||||
guidance_scale: number
|
||||
width: 128 | 192 | 256 | 320 | 384 | 448 | 512 | 576 | 640 | 704 | 768 | 832 | 896 | 960 | 1024;
|
||||
height: 128 | 192 | 256 | 320 | 384 | 448 | 512 | 576 | 640 | 704 | 768 | 832 | 896 | 960 | 1024;
|
||||
// allow_nsfw: boolean;
|
||||
turbo: boolean;
|
||||
use_cpu: boolean;
|
||||
use_full_precision: boolean;
|
||||
save_to_disk_path: null | string;
|
||||
use_face_correction: null | 'GFPGANv1.3';
|
||||
use_upscale: null| 'RealESRGAN_x4plus' | 'RealESRGAN_x4plus_anime_6B';
|
||||
show_only_filtered_image: boolean;
|
||||
init_image: undefined | string;
|
||||
prompt_strength: undefined | number;
|
||||
};
|
||||
|
||||
interface ImageCreateState {
|
||||
requestCount: number;
|
||||
requestOptions: ImageRequest;
|
||||
tags: string[];
|
||||
|
||||
setRequestCount: (count: number) => void;
|
||||
setRequestOptions: (key: keyof ImageRequest, value: any) => void;
|
||||
getValueForRequestKey: (key: keyof ImageRequest) => any;
|
||||
|
||||
toggleTag: (tag: string) => void;
|
||||
hasTag: (tag: string) => boolean;
|
||||
selectedTags:() => string[]
|
||||
builtRequest: () => ImageRequest;
|
||||
|
||||
uiOptions: ImageCreationUiOptions;
|
||||
toggleAdvancedSettingsIsOpen: () => void;
|
||||
toggleImageModifiersIsOpen: () => void;
|
||||
toggleUseUpscaling: () => void;
|
||||
isUsingUpscaling: () => boolean;
|
||||
toggleUseFaceCorrection: () => void;
|
||||
isUsingFaceCorrection: () => boolean;
|
||||
toggleUseRandomSeed: () => void;
|
||||
isRandomSeed: () => boolean;
|
||||
toggleUseAutoSave: () => void;
|
||||
isUseAutoSave: () => boolean;
|
||||
toggleSoundEnabled: () => void;
|
||||
isSoundEnabled: () => boolean;
|
||||
}
|
||||
|
||||
// devtools breaks TS
|
||||
// @ts-ignore
|
||||
export const useImageCreate = create<ImageCreateState>(devtools((set, get) => ({
|
||||
|
||||
requestCount: 1,
|
||||
|
||||
requestOptions:{
|
||||
prompt: 'a photograph of an astronaut riding a horse',
|
||||
seed: useRandomSeed(),
|
||||
num_outputs: 1,
|
||||
num_inference_steps: 50,
|
||||
guidance_scale: 7.5,
|
||||
width: 512,
|
||||
height: 512,
|
||||
prompt_strength: 0.8,
|
||||
// allow_nsfw: false,
|
||||
turbo: true,
|
||||
use_cpu: false,
|
||||
use_full_precision: true,
|
||||
save_to_disk_path: 'null',
|
||||
use_face_correction: null,
|
||||
use_upscale: 'RealESRGAN_x4plus',
|
||||
show_only_filtered_image: false,
|
||||
} as ImageRequest,
|
||||
|
||||
tags: [] as string[],
|
||||
|
||||
setRequestCount: (count: number) => set(produce((state) => {
|
||||
state.requestCount = count;
|
||||
})),
|
||||
|
||||
setRequestOptions: (key: keyof ImageRequest, value: any) => {
|
||||
set( produce((state) => {
|
||||
state.requestOptions[key] = value;
|
||||
}))
|
||||
},
|
||||
|
||||
getValueForRequestKey: (key: keyof ImageRequest) => {
|
||||
return get().requestOptions[key];
|
||||
},
|
||||
|
||||
toggleTag: (tag: string) => {
|
||||
set( produce((state) => {
|
||||
const index = state.tags.indexOf(tag);
|
||||
if (index > -1) {
|
||||
state.tags.splice(index, 1);
|
||||
} else {
|
||||
state.tags.push(tag);
|
||||
}
|
||||
}))
|
||||
},
|
||||
|
||||
hasTag: (tag:string) => {
|
||||
return get().tags.indexOf(tag) > -1;
|
||||
},
|
||||
|
||||
selectedTags: () => {
|
||||
return get().tags;
|
||||
},
|
||||
|
||||
// the request body to send to the server
|
||||
// this is a computed value, just adding the tags to the request
|
||||
builtRequest: () => {
|
||||
|
||||
console.log('builtRequest');
|
||||
const state = get();
|
||||
const requestOptions = state.requestOptions;
|
||||
const tags = state.tags;
|
||||
|
||||
// join all the tags with a comma and add it to the prompt
|
||||
const prompt = `${requestOptions.prompt} ${tags.join(',')}`;
|
||||
console.log('builtRequest return1');
|
||||
|
||||
const request = {
|
||||
...requestOptions,
|
||||
prompt
|
||||
}
|
||||
// if we arent using auto save clear the save path
|
||||
if(!state.uiOptions.isUseAutoSave){
|
||||
// maybe this is "None" ?
|
||||
// TODO check this
|
||||
request.save_to_disk_path = null;
|
||||
}
|
||||
console.log('builtRequest return2');
|
||||
// if we arent using face correction clear the face correction
|
||||
if(!state.uiOptions.isCheckUseFaceCorrection){
|
||||
request.use_face_correction = null;
|
||||
}
|
||||
console.log('builtRequest return3');
|
||||
// if we arent using upscaling clear the upscaling
|
||||
if(!state.uiOptions.isCheckedUseUpscaling){
|
||||
request.use_upscale = null;
|
||||
}
|
||||
|
||||
// const request = {
|
||||
// ...requestOptions,
|
||||
// prompt
|
||||
// }
|
||||
console.log('builtRequest return last');
|
||||
return request;
|
||||
},
|
||||
|
||||
uiOptions: {
|
||||
// TODO proper persistence of all UI / user settings centrally somewhere?
|
||||
// localStorage.getItem('ui:advancedSettingsIsOpen') === 'true',
|
||||
advancedSettingsIsOpen:false,
|
||||
imageModifierIsOpen: false,
|
||||
isCheckedUseUpscaling: false,
|
||||
isCheckUseFaceCorrection: true,
|
||||
isUseRandomSeed: true,
|
||||
isUseAutoSave: false,
|
||||
isSoundEnabled: true,
|
||||
},
|
||||
|
||||
toggleAdvancedSettingsIsOpen: () => {
|
||||
set( produce((state) => {
|
||||
state.uiOptions.advancedSettingsIsOpen = !state.uiOptions.advancedSettingsIsOpen;
|
||||
localStorage.setItem('ui:advancedSettingsIsOpen', state.uiOptions.advancedSettingsIsOpen);
|
||||
}))
|
||||
},
|
||||
|
||||
toggleImageModifiersIsOpen: () => {
|
||||
set( produce((state) => {
|
||||
state.uiOptions.imageModifierIsOpen = !state.uiOptions.imageModifierIsOpen;
|
||||
localStorage.setItem('ui:imageModifierIsOpen', state.uiOptions.imageModifierIsOpen);
|
||||
}))
|
||||
},
|
||||
|
||||
toggleUseUpscaling: () => {
|
||||
set( produce((state) => {
|
||||
state.uiOptions.isCheckedUseUpscaling = !state.uiOptions.isCheckedUseUpscaling;
|
||||
state.requestOptions.use_upscale = state.uiOptions.isCheckedUseUpscaling ? 'RealESRGAN_x4plus' : null;
|
||||
localStorage.setItem('ui:isCheckedUseUpscaling', state.uiOptions.isCheckedUseUpscaling);
|
||||
}))
|
||||
},
|
||||
|
||||
isUsingUpscaling: () => {
|
||||
return get().uiOptions.isCheckedUseUpscaling;
|
||||
},
|
||||
|
||||
toggleUseFaceCorrection: () => {
|
||||
set( produce((state) => {
|
||||
state.uiOptions.isCheckUseFaceCorrection = !state.uiOptions.isCheckUseFaceCorrection;
|
||||
state.use_face_correction = state.uiOptions.isCheckUseFaceCorrection ? 'GFPGANv1.3' : null;
|
||||
localStorage.setItem('ui:isCheckUseFaceCorrection', state.uiOptions.isCheckUseFaceCorrection);
|
||||
}))
|
||||
},
|
||||
|
||||
isUsingFaceCorrection: () => {
|
||||
return get().uiOptions.isCheckUseFaceCorrection;
|
||||
},
|
||||
|
||||
|
||||
toggleUseRandomSeed: () => {
|
||||
set( produce((state) => {
|
||||
state.uiOptions.isUseRandomSeed = !state.uiOptions.isUseRandomSeed;
|
||||
state.requestOptions.seed = state.uiOptions.isUseRandomSeed ? useRandomSeed() : state.requestOptions.seed;
|
||||
localStorage.setItem('ui:isUseRandomSeed', state.uiOptions.isUseRandomSeed);
|
||||
}))
|
||||
},
|
||||
|
||||
isRandomSeed: () => {
|
||||
return get().uiOptions.isUseRandomSeed;
|
||||
},
|
||||
|
||||
toggleUseAutoSave: () => {
|
||||
//isUseAutoSave
|
||||
//save_to_disk_path
|
||||
set( produce((state) => {
|
||||
state.uiOptions.isUseAutoSave = !state.uiOptions.isUseAutoSave;
|
||||
localStorage.setItem('ui:isUseAutoSave', state.uiOptions.isUseAutoSave);
|
||||
}))
|
||||
},
|
||||
|
||||
isUseAutoSave: () => {
|
||||
return get().uiOptions.isUseAutoSave;
|
||||
},
|
||||
|
||||
toggleSoundEnabled: () => {
|
||||
set( produce((state) => {
|
||||
state.uiOptions.isSoundEnabled = !state.uiOptions.isSoundEnabled;
|
||||
//localStorage.setItem('ui:isSoundEnabled', state.uiOptions.isSoundEnabled);
|
||||
}))
|
||||
},
|
||||
|
||||
isSoundEnabled: () => {
|
||||
return get().uiOptions.isSoundEnabled;
|
||||
},
|
||||
|
||||
})));
|
||||
|
||||
|
22
ui/frontend/build_src/src/store/imageDisplayStore.ts
Normal file
22
ui/frontend/build_src/src/store/imageDisplayStore.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import create from 'zustand';
|
||||
import produce from 'immer';
|
||||
|
||||
// import { devtools } from 'zustand/middleware'
|
||||
|
||||
interface ImageDisplayState {
|
||||
imageOptions: Map<string, any>;
|
||||
currentImage: object | null;
|
||||
addNewImage: (ImageData: string, imageOptions: any) => void
|
||||
}
|
||||
|
||||
export const useImageDisplay = create<ImageDisplayState>((set) => ({
|
||||
imageOptions: new Map<string, any>(),
|
||||
currentImage: null,
|
||||
// use produce to make sure we don't mutate state
|
||||
addNewImage: (ImageData: string, imageOptions: any) => {
|
||||
set( produce((state) => {
|
||||
state.currentImage = { display: ImageData, options: imageOptions };
|
||||
state.images.set(ImageData, imageOptions)
|
||||
}));
|
||||
}
|
||||
}));
|
48
ui/frontend/build_src/src/store/imageQueueStore.ts
Normal file
48
ui/frontend/build_src/src/store/imageQueueStore.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import create from 'zustand';
|
||||
import produce from 'immer';
|
||||
import { useRandomSeed } from '../utils';
|
||||
|
||||
import { imageOptions } from './imageCreateStore';
|
||||
|
||||
interface ImageQueueState {
|
||||
images : imageOptions[];
|
||||
completedImageIds: string[];
|
||||
addNewImage: (id:string, imageOptions: imageOptions) => void
|
||||
hasQueuedImages: () => boolean;
|
||||
firstInQueue: () => imageOptions | [];
|
||||
removeFirstInQueue: () => void;
|
||||
}
|
||||
|
||||
// figure out why TS is complaining about this
|
||||
// @ts-ignore
|
||||
export const useImageQueue = create<ImageQueueState>((set, get) => ({
|
||||
images: new Array(),
|
||||
completedImageIds: new Array(),
|
||||
// use produce to make sure we don't mutate state
|
||||
addNewImage: (id: string, imageOptions: any) => {
|
||||
set( produce((state) => {
|
||||
|
||||
let { seed } = imageOptions;
|
||||
if (imageOptions.isSeedRandom) {
|
||||
seed = useRandomSeed();
|
||||
}
|
||||
|
||||
state.images.push({ id, options: {...imageOptions, seed} });
|
||||
}));
|
||||
},
|
||||
|
||||
hasQueuedImages: () => {
|
||||
return get().images.length > 0;
|
||||
},
|
||||
firstInQueue: () => {
|
||||
return get().images[0] as imageOptions || [];
|
||||
},
|
||||
|
||||
removeFirstInQueue: () => {
|
||||
set( produce((state) => {
|
||||
const image = state.images.shift();
|
||||
state.completedImageIds.push(image.id);
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
3
ui/frontend/build_src/src/utils.ts
Normal file
3
ui/frontend/build_src/src/utils.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function useRandomSeed(){
|
||||
return Math.floor(Math.random() * 10000);
|
||||
};
|
1
ui/frontend/build_src/src/vite-env.d.ts
vendored
Normal file
1
ui/frontend/build_src/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
21
ui/frontend/build_src/tsconfig.json
Normal file
21
ui/frontend/build_src/tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
9
ui/frontend/build_src/tsconfig.node.json
Normal file
9
ui/frontend/build_src/tsconfig.node.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
24
ui/frontend/build_src/vite.config.ts
Normal file
24
ui/frontend/build_src/vite.config.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 9001,
|
||||
},
|
||||
build: {
|
||||
// make sure everythign is in the same directory
|
||||
outDir: '../dist',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// dont hash the file names
|
||||
// maybe once we update the python server?
|
||||
entryFileNames: `[name].js`,
|
||||
chunkFileNames: `[name].js`,
|
||||
assetFileNames: `[name].[ext]`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
})
|
BIN
ui/frontend/dist/ding.mp3
vendored
Normal file
BIN
ui/frontend/dist/ding.mp3
vendored
Normal file
Binary file not shown.
1
ui/frontend/dist/index.css
vendored
Normal file
1
ui/frontend/dist/index.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.App{position:relative;width:100%;height:100%;pointer-events:auto;display:grid;background-color:#202124;grid-template-columns:360px 1fr;grid-template-rows:100px 1fr 300px;grid-template-areas:"header header header" "create display display" "footer footer footer"}@media screen and (max-width: 768px){.App{grid-template-columns:1fr;grid-template-rows:100px 1fr 1fr 300px;grid-template-areas:"header" "create" "display" "footer"}}.header-layout{grid-area:header}.create-layout{grid-area:create}.display-layout{grid-area:display}.footer-layout{grid-area:footer}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.mb-4{margin-bottom:1rem}.starting{color:#f0ad4e}.error{color:#d9534f}.success{color:#5cb85c}.header-display{color:#fff;display:flex;align-items:center;justify-content:center}.status-display{margin-left:10px}input[size="4"]{width:4.5rem}.panel-box-toggle-btn{display:block;width:100%;text-align:left;background-color:transparent;color:#fff;border:0 none;cursor:pointer}.selected-tags{margin:10px 0}.selected-tags ul{margin:0;padding:0;display:flex;flex-wrap:wrap}li{list-style:none}.modifier-list{display:flex;flex-wrap:wrap;margin:0;padding:0}.modifierTag{display:inline-block;padding:6px;background-color:#264d8d;color:#fff;border-radius:5px;margin:5px}.modifierTag.selected{background-color:#830b79}.modifierTag p{margin:0}.footer-display{color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center}body{margin:0;min-width:320px;min-height:100vh}#root{position:absolute;top:0;left:0;width:100vw;height:100vh;z-index:1}
|
1265
ui/frontend/dist/index.html
vendored
Normal file
1265
ui/frontend/dist/index.html
vendored
Normal file
File diff suppressed because it is too large
Load Diff
74
ui/frontend/dist/index.js
vendored
Normal file
74
ui/frontend/dist/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
ui/frontend/dist/kofi.png
vendored
Normal file
BIN
ui/frontend/dist/kofi.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
151
ui/frontend/dist/modifiers.json
vendored
Normal file
151
ui/frontend/dist/modifiers.json
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
[
|
||||
[
|
||||
"Drawing Style",
|
||||
[
|
||||
"Cel Shading",
|
||||
"Children's Drawing",
|
||||
"Crosshatch",
|
||||
"Detailed and Intricate",
|
||||
"Doodle",
|
||||
"Dot Art",
|
||||
"Line Art",
|
||||
"Sketch"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Visual Style",
|
||||
[
|
||||
"2D",
|
||||
"8-bit",
|
||||
"16-bit",
|
||||
"Anaglyph",
|
||||
"Anime",
|
||||
"CGI",
|
||||
"Cartoon",
|
||||
"Comic Book",
|
||||
"Concept Art",
|
||||
"Digital Art",
|
||||
"Fantasy",
|
||||
"Graphic Novel",
|
||||
"Hard Edge Painting",
|
||||
"Hydrodipped",
|
||||
"Lithography",
|
||||
"Manga",
|
||||
"Modern Art",
|
||||
"Mosaic",
|
||||
"Mural",
|
||||
"Photo",
|
||||
"Realistic",
|
||||
"Street Art",
|
||||
"Visual Novel",
|
||||
"Watercolor"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Pen",
|
||||
[
|
||||
"Chalk",
|
||||
"Colored Pencil",
|
||||
"Graphite",
|
||||
"Ink",
|
||||
"Oil Paint",
|
||||
"Pastel Art"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Carving and Etching",
|
||||
[
|
||||
"Etching",
|
||||
"Linocut",
|
||||
"Paper Model",
|
||||
"Paper-Mache",
|
||||
"Papercutting",
|
||||
"Pyrography",
|
||||
"Wood-Carving"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Camera",
|
||||
[
|
||||
"Aerial View",
|
||||
"Canon50",
|
||||
"Cinematic",
|
||||
"Close-up",
|
||||
"Color Grading",
|
||||
"Dramatic",
|
||||
"Film Grain",
|
||||
"Fisheye Lens",
|
||||
"Glamor Shot",
|
||||
"Golden Hour",
|
||||
"HD",
|
||||
"Lens Flare",
|
||||
"Macro",
|
||||
"Polaroid",
|
||||
"Vintage",
|
||||
"War Photography",
|
||||
"White Balance",
|
||||
"Wildlife Photography"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Color",
|
||||
[
|
||||
"Beautiful Lighting",
|
||||
"Colorful",
|
||||
"Dynamic Lighting",
|
||||
"Electric Colors",
|
||||
"Infrared",
|
||||
"Synthwave",
|
||||
"Warm Color Palette"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Emotions",
|
||||
[
|
||||
"Angry",
|
||||
"Disgusted",
|
||||
"Embarrassed",
|
||||
"Evil",
|
||||
"Excited",
|
||||
"Fear",
|
||||
"Happy",
|
||||
"Lonely",
|
||||
"Sad",
|
||||
"Surprised"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Style of an artist or community",
|
||||
[
|
||||
"Artstation",
|
||||
"by Agnes Lawrence Pelton",
|
||||
"by Akihito Yoshida",
|
||||
"by Andy Warhol",
|
||||
"by Artgerm",
|
||||
"by Asaf Hanuka",
|
||||
"by Aubrey Beardsley",
|
||||
"by Banksy",
|
||||
"by Ben Enwonwu",
|
||||
"by Caravaggio Michelangelo Merisi",
|
||||
"by David Mann",
|
||||
"by Frida Kahlo",
|
||||
"by H.R. Giger",
|
||||
"by Hayao Miyazaki",
|
||||
"by Ivan Shishkin",
|
||||
"by Johannes Vermeer",
|
||||
"by John William Waterhouse",
|
||||
"by Katsushika Hokusai",
|
||||
"by Ko Young Hoon",
|
||||
"by Leonardo da Vinci",
|
||||
"by Lisa Frank",
|
||||
"by Mahmoud Saïd",
|
||||
"by Mark Brooks",
|
||||
"by Pablo Picasso",
|
||||
"by Richard Dadd",
|
||||
"by Salvador Dalí",
|
||||
"by Tivadar Csontváry Kosztka",
|
||||
"by Yoshitaka Amano",
|
||||
"by wlop"
|
||||
]
|
||||
]
|
||||
]
|
38
ui/server.py
38
ui/server.py
@ -18,11 +18,24 @@ from fastapi import FastAPI, HTTPException
|
||||
from starlette.responses import FileResponse, StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
import logging
|
||||
|
||||
# this is needed for development.
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from sd_internal import Request, Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# we need to be able to run a local server for the UI (9001)
|
||||
# and still be able to hit our python port (9000)
|
||||
origins = ["*"]
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
model_loaded = False
|
||||
model_is_loading = False
|
||||
|
||||
@ -58,7 +71,18 @@ class SetAppConfigRequest(BaseModel):
|
||||
@app.get('/')
|
||||
def read_root():
|
||||
headers = {"Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0"}
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'index.html'), headers=headers)
|
||||
#return FileResponse(os.path.join(SD_UI_DIR, 'index.html'), headers=headers)
|
||||
return FileResponse(os.path.join(SD_UI_DIR,'frontend/dist/index.html'), headers=headers)
|
||||
|
||||
# then get the js files
|
||||
@app.get('/index.js')
|
||||
def read_scripts():
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'frontend/dist/index.js'))
|
||||
|
||||
#then get the css files
|
||||
@app.get('/index.css')
|
||||
def read_styles():
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'frontend/dist/index.css'))
|
||||
|
||||
@app.get('/ping')
|
||||
async def ping():
|
||||
@ -180,13 +204,15 @@ def getAppConfig():
|
||||
print(traceback.format_exc())
|
||||
return HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.get('/media/ding.mp3')
|
||||
# moved these to the root for easier pathing
|
||||
# TODO: change the vite config for public files
|
||||
@app.get('/ding.mp3')
|
||||
def read_ding():
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'media/ding.mp3'))
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'frontend/dist/ding.mp3'))
|
||||
|
||||
@app.get('/media/kofi.png')
|
||||
@app.get('/kofi.png')
|
||||
def read_modifiers():
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'media/kofi.png'))
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'frontend/dist/kofi.png'))
|
||||
|
||||
@app.get('/modifiers.json')
|
||||
def read_modifiers():
|
||||
|
Loading…
Reference in New Issue
Block a user