Merge pull request #294 from cmdr2/main

Merge from main
This commit is contained in:
cmdr2 2022-10-07 20:03:01 +05:30 committed by GitHub
commit 1e3b4f9969
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
531 changed files with 478 additions and 17640 deletions

1
.gitattributes vendored
View File

@ -1 +0,0 @@
* text=auto eol=lf

6
.gitignore vendored
View File

@ -2,10 +2,4 @@ __pycache__
installer
installer.tar
dist
# built code for the front end
!/ui/frontend/dist
ui/frontend/.idea/*
ui/frontend/build_src/.idea/*
.idea/*

View File

@ -8,7 +8,7 @@
[![Discord Server](https://img.shields.io/discord/1014774730907209781?label=Discord)](https://discord.com/invite/u9yhsFmEkB) (for support, and development discussion) | [Troubleshooting guide for common problems](Troubleshooting.md)
️‍🔥🎉 **New!** Task Queue, Negative Prompt, Live Preview, More Samplers, In-Painting, Face Correction (GFPGAN) and Upscaling (RealESRGAN) have been added!
️‍🔥🎉 **New!** Use Custom Weights, Task Queue, Negative Prompt, Live Preview, More Samplers, In-Painting, Face Correction (GFPGAN) and Upscaling (RealESRGAN) have been added!
This distribution currently uses Stable Diffusion 1.4. Once the model for 1.5 becomes publicly available, the model in this distribution will be updated.
@ -18,6 +18,7 @@ This distribution currently uses Stable Diffusion 1.4. Once the model for 1.5 be
- **In-Painting**
- **Live Preview**: See the image as the AI is drawing it
- **Task Queue**: Queue up all your ideas, without waiting for the current task to finish
- **Custom Weights**: Use your own `.ckpt` file, by placing it inside the `stable-diffusion` folder (rename it to `custom-model.ckpt`)
- **Negative Prompt**: Specify aspects of the image to *remove*.
- **Lots of Samplers:** ddim, plms, heun, euler, euler_a, dpm2, dpm2_a, lms
- **Image Modifiers**: A library of *modifier tags* like *"Realistic"*, *"Pencil Sketch"*, *"ArtStation"* etc. Experiment with various styles quickly.

View File

@ -64,7 +64,7 @@ Users don't need to have the Anaconda Prompt installed to do this anymore, since
5. Type `conda activate .\env` and press enter
6. Type `python --version` and press enter. You should see 3.8.5.
**Windows:**
**Linux:**
1. Open the terminal
2. Type `cd /path/to/stable-diffusion-ui` and press enter
3. Type `installer/bin/activate` and press enter

View File

@ -0,0 +1,14 @@
@echo off
echo "Opening Stable Diffusion UI - Developer Console.." & echo.
@call installer\Scripts\activate.bat
@call conda-unpack
@call conda --version
@call git --version
@call conda activate .\stable-diffusion\env
cmd /k

View File

@ -0,0 +1,17 @@
#!/bin/bash
if [ "$0" == "bash" ]; then
echo "Opening Stable Diffusion UI - Developer Console.."
echo ""
source installer/bin/activate
conda-unpack
conda --version
git --version
conda activate ./stable-diffusion/env
else
bash --init-file open_dev_console.sh
fi

View File

@ -55,6 +55,7 @@ if "%update_branch%"=="" (
@xcopy sd-ui-files\ui ui /s /i /Y
@copy sd-ui-files\scripts\on_sd_start.bat scripts\ /Y
@copy "sd-ui-files\scripts\Start Stable Diffusion UI.cmd" . /Y
@copy "sd-ui-files\scripts\Developer Console.cmd" . /Y
@call scripts\on_sd_start.bat

View File

@ -37,6 +37,7 @@ rm -rf ui
cp -Rf sd-ui-files/ui .
cp sd-ui-files/scripts/on_sd_start.sh scripts/
cp sd-ui-files/scripts/start.sh .
cp sd-ui-files/scripts/developer_console.sh .
./scripts/on_sd_start.sh

View File

@ -5,6 +5,9 @@
@REM Caution, this file will make your eyes and brain bleed. It's such an unholy mess.
@REM Note to self: Please rewrite this in Python. For the sake of your own sanity.
@copy "sd-ui-files\scripts\Developer Console.cmd" . /Y
if exist "Open Developer Console.cmd" del "Open Developer Console.cmd"
@call python -c "import os; import shutil; frm = 'sd-ui-files\\ui\\hotfix\\9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');"
@>nul grep -c "sd_git_cloned" scripts\install_status.txt
@ -163,6 +166,9 @@ call WHERE uvicorn > .tmp
if not exist "..\models\stable-diffusion" mkdir "..\models\stable-diffusion"
echo. > "..\models\stable-diffusion\Put your custom ckpt files here.txt"
@if exist "sd-v1-4.ckpt" (
for %%I in ("sd-v1-4.ckpt") do if "%%~zI" EQU "4265380512" (
echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the HuggingFace 4 GB Model."

View File

@ -4,6 +4,11 @@ cp sd-ui-files/scripts/on_env_start.sh scripts/
source installer/etc/profile.d/conda.sh
cp sd-ui-files/scripts/developer_console.sh .
if [ -e "open_dev_console.sh" ]; then
rm "open_dev_console.sh"
fi
python -c "import os; import shutil; frm = 'sd-ui-files/ui/hotfix/9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'; dst = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'transformers', '9c24e6cd9f499d02c4f21a033736dabd365962dc80fe3aeb57a8f85ea45a20a3.26fead7ea4f0f843f6eb4055dfd25693f1a71f3c6871b184042d4b126244e142'); shutil.copyfile(frm, dst) if os.path.exists(dst) else print(''); print('Hotfixed broken JSON file from OpenAI');"
# Caution, this file will make your eyes and brain bleed. It's such an unholy mess.
@ -159,8 +164,11 @@ fi
mkdir -p "../models/stable-diffusion"
echo "" > "../models/stable-diffusion/Put your custom ckpt files here.txt"
if [ -f "sd-v1-4.ckpt" ]; then
model_size=`ls -l sd-v1-4.ckpt | awk '{print $5}'`
model_size=`find "sd-v1-4.ckpt" -printf "%s"`
if [ "$model_size" -eq "4265380512" ] || [ "$model_size" -eq "7703807346" ] || [ "$model_size" -eq "7703810927" ]; then
echo "Data files (weights) necessary for Stable Diffusion were already downloaded"
@ -176,7 +184,7 @@ if [ ! -f "sd-v1-4.ckpt" ]; then
curl -L -k https://me.cmdr2.org/stable-diffusion-ui/sd-v1-4.ckpt > sd-v1-4.ckpt
if [ -f "sd-v1-4.ckpt" ]; then
model_size=`ls -l sd-v1-4.ckpt | awk '{print $5}'`
model_size=`find "sd-v1-4.ckpt" -printf "%s"`
if [ ! "$model_size" == "4265380512" ]; then
printf "\n\nError: The downloaded model file was invalid! Bytes downloaded: $model_size\n\n"
printf "\n\nError downloading the data files (weights) for Stable Diffusion. Sorry about that, please try to:\n 1. Run this installer again.\n 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/blob/main/Troubleshooting.md\n 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB\n 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues\nThanks!\n\n"
@ -192,7 +200,7 @@ fi
if [ -f "GFPGANv1.3.pth" ]; then
model_size=`ls -l GFPGANv1.3.pth | awk '{print $5}'`
model_size=`find "GFPGANv1.3.pth" -printf "%s"`
if [ "$model_size" -eq "348632874" ]; then
echo "Data files (weights) necessary for GFPGAN (Face Correction) were already downloaded"
@ -208,7 +216,7 @@ if [ ! -f "GFPGANv1.3.pth" ]; then
curl -L -k https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth > GFPGANv1.3.pth
if [ -f "GFPGANv1.3.pth" ]; then
model_size=`ls -l GFPGANv1.3.pth | awk '{print $5}'`
model_size=`find "GFPGANv1.3.pth" -printf "%s"`
if [ ! "$model_size" -eq "348632874" ]; then
printf "\n\nError: The downloaded GFPGAN model file was invalid! Bytes downloaded: $model_size\n\n"
printf "\n\nError downloading the data files (weights) for GFPGAN (Face Correction). Sorry about that, please try to:\n 1. Run this installer again.\n 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/blob/main/Troubleshooting.md\n 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB\n 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues\nThanks!\n\n"
@ -224,7 +232,7 @@ fi
if [ -f "RealESRGAN_x4plus.pth" ]; then
model_size=`ls -l RealESRGAN_x4plus.pth | awk '{print $5}'`
model_size=`find "RealESRGAN_x4plus.pth" -printf "%s"`
if [ "$model_size" -eq "67040989" ]; then
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus were already downloaded"
@ -240,7 +248,7 @@ if [ ! -f "RealESRGAN_x4plus.pth" ]; then
curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth > RealESRGAN_x4plus.pth
if [ -f "RealESRGAN_x4plus.pth" ]; then
model_size=`ls -l RealESRGAN_x4plus.pth | awk '{print $5}'`
model_size=`find "RealESRGAN_x4plus.pth" -printf "%s"`
if [ ! "$model_size" -eq "67040989" ]; then
printf "\n\nError: The downloaded ESRGAN x4plus model file was invalid! Bytes downloaded: $model_size\n\n"
printf "\n\nError downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus. Sorry about that, please try to:\n 1. Run this installer again.\n 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/blob/main/Troubleshooting.md\n 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB\n 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues\nThanks!\n\n"
@ -256,7 +264,7 @@ fi
if [ -f "RealESRGAN_x4plus_anime_6B.pth" ]; then
model_size=`ls -l RealESRGAN_x4plus_anime_6B.pth | awk '{print $5}'`
model_size=`find "RealESRGAN_x4plus_anime_6B.pth" -printf "%s"`
if [ "$model_size" -eq "17938799" ]; then
echo "Data files (weights) necessary for ESRGAN (Resolution Upscaling) x4plus_anime were already downloaded"
@ -272,7 +280,7 @@ if [ ! -f "RealESRGAN_x4plus_anime_6B.pth" ]; then
curl -L -k https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth > RealESRGAN_x4plus_anime_6B.pth
if [ -f "RealESRGAN_x4plus_anime_6B.pth" ]; then
model_size=`ls -l RealESRGAN_x4plus_anime_6B.pth | awk '{print $5}'`
model_size=`find "RealESRGAN_x4plus_anime_6B.pth" -printf "%s"`
if [ ! "$model_size" -eq "17938799" ]; then
printf "\n\nError: The downloaded ESRGAN x4plus_anime model file was invalid! Bytes downloaded: $model_size\n\n"
printf "\n\nError downloading the data files (weights) for ESRGAN (Resolution Upscaling) x4plus_anime. Sorry about that, please try to:\n 1. Run this installer again.\n 2. If that doesn't fix it, please try the common troubleshooting steps at https://github.com/cmdr2/stable-diffusion-ui/blob/main/Troubleshooting.md\n 3. If those steps don't help, please copy *all* the error messages in this window, and ask the community at https://discord.com/invite/u9yhsFmEkB\n 4. If that doesn't solve the problem, please file an issue at https://github.com/cmdr2/stable-diffusion-ui/issues\nThanks!\n\n"

File diff suppressed because it is too large Load Diff

View File

@ -1,87 +0,0 @@
const path = require("path");
module.exports = {
env: {
browser: true,
es2021: true,
},
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
tsconfigRootDir: __dirname,
},
plugins: ["react"],
extends: [
"prettier",
"plugin:react/recommended",
"standard-with-typescript",
"plugin:i18next/recommended",
"plugin:i18n-json/recommended",
],
settings: {
react: {
version: "detect",
},
},
rules: {
// general things turned off for now
"no-debugger": "warn",
"eol-last": "off",
"comma-dangle": ["off", "always-multiline"],
"no-void": ["off"],
"array-callback-return": ["off"],
"spaced-comment": ["off"],
"padded-blocks": ["off"],
"no-multiple-empty-lines": ["off", { max: 2, maxEOF: 1 }],
quotes: ["off", "double"],
semi: ["off", "always"],
yoda: ["off"],
eqeqeq: ["off"],
"react/display-name": "warn",
// TS THINGS WE DONT WANT
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/ban-ts-comment": "off",
// these are things that fight with prettier
"@typescript-eslint/comma-dangle": "off",
"@typescript-eslint/space-before-function-paren": "off",
"@typescript-eslint/quotes": "off",
"@typescript-eslint/semi": "off",
"@typescript-eslint/brace-style": "off",
"@typescript-eslint/indent": "off",
"@typescript-eslint/member-delimiter-style": "off",
// TS WARNINGS WE WANT
"@typescript-eslint/no-unused-vars": "warn",
"@typescript-eslint/no-non-null-assertion": "warn",
// i18n stuff no string literal works but turned off for now
"i18next/no-literal-string": "off",
// still need to figure out how to get this to work
// it should error if we dont haev all the keys in the translation file
"i18n-json/identical-keys": [
"error",
{
filePath: {
"home.json/": path.resolve("./Translation/locales/en/home.json"),
},
},
],
},
overrides: [
{
files: ["*.ts", "*.tsx"],
parserOptions: {
project: ["./tsconfig.json"], // Specify it only for TypeScript files
},
},
],
// eslint-disable-next-line semi
};

View File

@ -1,18 +0,0 @@
# 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

View File

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="/media/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/media/favicon-32x32.png" sizes="32x32">
<meta name="color-scheme" content="dark light" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<title>Stable Diffusion UI</title>
</head>
<body>
<!-- The react app entry point. Currently no ui just poc importing and logging -->
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,56 +0,0 @@
{
"name": "react-ts",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"pretty": "prettier --write .",
"dev": "vite",
"build": "tsc && vite build --emptyOutDir",
"preview": "vite preview"
},
"dependencies": {
"@headlessui/react": "^1.7.2",
"@tanstack/react-location": "^3.7.4",
"@tanstack/react-query": "^4.2.3",
"@tanstack/react-query-devtools": "^4.2.3",
"@vanilla-extract/css": "^1.9.0",
"@vanilla-extract/css-utils": "^0.1.2",
"@vanilla-extract/recipes": "^0.2.5",
"@vanilla-extract/vite-plugin": "^3.5.0",
"i18next": "^21.9.2",
"immer": "^9.0.15",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^11.18.6",
"uuid": "^9.0.0",
"zustand": "^4.1.1"
},
"devDependencies": {
"@types/node": "^18.7.18",
"@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.37.0",
"@typescript-eslint/parser": "^5.37.0",
"@vitejs/plugin-react": "^2.0.1",
"eslint": "^8.23.1",
"eslint-config-prettier": "^8.5.0",
"eslint-config-standard-with-typescript": "^23.0.0",
"eslint-plugin-i18n-json": "^4.0.0",
"eslint-plugin-i18next": "^6.0.0-4",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.2.5",
"eslint-plugin-promise": "^6.0.1",
"eslint-plugin-react": "^7.31.8",
"prettier": "^2.7.1",
"typescript": "^4.8.3",
"vite": "^3.0.7",
"vite-plugin-eslint": "^1.8.1"
},
"overrides": {
"@vanilla-extract/vite-plugin": {
"vite": "^3"
}
}
}

View File

@ -1,7 +0,0 @@
module.exports = {
singleQuote: true,
tabWidth: 2,
semi: true,
trailingComma: "es5",
endOfLine: "lf",
};

View File

@ -1,21 +0,0 @@
import React from "react";
import { ReactLocation, Router } from "@tanstack/react-location";
import Home from "./pages/Home";
import Settings from "./pages/Settings";
import "./Translation/config";
const location = new ReactLocation();
function App() {
return (
<Router
location={location}
routes={[
{ path: "/", element: <Home /> },
{ path: "/settings", element: <Settings /> },
]}
></Router>
);
}
export default App;

View File

@ -1,32 +0,0 @@
import i18n from "i18next";
// this should be updated to an interface
import ENTranslation from "./locales/en/home.json";
import ESTranslation from "./locales/es/home.json";
import { initReactI18next } from "react-i18next";
export const resources = {
en: {
translation: ENTranslation,
},
es: {
translation: ESTranslation,
},
} as const;
i18n
.use(initReactI18next)
.init({
lng: "en",
interpolation: {
escapeValue: false,
},
resources,
})
.then(() => {
console.log("i18n initialized");
})
.catch((err) => {
console.error("i18n initialization failed", err);
})
.finally(() => {
console.log("i18n initialization finished");
});

View File

@ -1,110 +0,0 @@
{
"title": "Stable Diffusion UI",
"description": "",
"navbar": {
"home": "Home",
"history": "History",
"community": "Community",
"settings": "Settings"
},
"land-cre": {
"cp": "Create Profile",
"cp-place": "Profile name",
"pp": "Profile Picture",
"pp-disc": "",
"ast": "Automatically save to",
"ast-disc": "File path to auto save your creations",
"place": "File path",
"cre": "Create"
},
"land-pre": {
"user": "Username",
"add": "Add Profile"
},
"home": {
"status-starting": "Stable Diffusion is starting...",
"status-ready": "Stable Diffusion is ready to use!",
"status-error": "Stable Diffusion is not running!",
"editor-title": "Prompt",
"initial-img-txt": "Initial Image: (optional)",
"initial-img-btn": "Browse...",
"initial-img-text2": "No file selected.",
"make-img-btn": "Make Image",
"make-img-btn-stop": "Stop"
},
"in-paint": {
"txt": "In-Painting (select the area which the AI will paint into)",
"clear": "Clear"
},
"settings": {
"base-img": "Use base image:",
"seed": "Seed:",
"amount-of-img": "Amount of images to make:",
"how-many": "How many at once:",
"stream-img": "Stream images (this will slow down image generation):",
"width": "Width:",
"height": "Height:",
"sampler": "Sampler:",
"steps": "Number of inference steps:",
"guide-scale": "Guidance Scale:",
"prompt-str": "Prompt Strength:",
"live-preview": "Show a live preview of the image (disable this for faster image generation)",
"fix-face": "Fix incorrect faces and eyes (uses GFPGAN)",
"ups": "Upscale the image to 4x resolution using:",
"no-ups": "No Upscaling",
"corrected": "Show only the corrected/upscaled image"
},
"tags": {
"txt": "Image Modifiers (art styles, tags etc)"
},
"preview-prompt": {
"part1": "Type a prompt and press the \"Make Image\" button.",
"part2": "You can set an \"Initial Image\" if you want to guide the AI.\n",
"part3": "You can also add modifiers like \"Realistic\", \"Pencil Sketch\", \"ArtStation\" etc by browsing through the \"Image Modifiers\" section and selecting the desired modifiers.\n",
"part4": "Click \"Advanced Settings\" for additional settings like seed, image size, number of images to generate etc.",
"part5": "Enjoy! :)"
},
"current-task": "Current task",
"recent-create": "Recently Created",
"popup": {
"use-btn": "Use Image",
"use-btn2": "Use Image and Tags"
},
"history": {
"fave": "Favorites Only",
"search": "Search"
},
"advanced-settings": {
"sound": "Play sound on task completion",
"sound-disc": "Will play a sound so user can hear when image is done.",
"turbo": "Turbo mode",
"turbo-disc": "Generates images faster, but uses an additional 1 GB of GPU memory",
"cpu": "Use CPU instead of GPU",
"cpu-disc": "Warning: this will be *very* slow",
"gpu": "Use full precision",
"gpu-disc": "(for GPU-only. warning: this will consume more VRAM)",
"beta": "Beta Features",
"beta-disc": "Get the latest features immediately (but could be less stable). \nPlease restart the program after changing this.",
"save": "SAVE"
},
"storage": {
"ast": "Automatically save to",
"ast-disc": "File path to auto save your creations",
"place": "File path",
"cps": "Cross profile sharing",
"cps-disc": "Profiles will see suggestions from each other.",
"acb": "Allow cloud backup",
"acb-disc": "A button will show up for images on hover",
"acb-place": "Choose your",
"acc-api": "Api key",
"acb-api-place": "Your API key",
"save": "SAVE"
},
"import": {
"imp-btn": "IMPORT",
"exp-btn": "EXPORT",
"disc": "It is a good idea to leave the exported file as it is. Otherwise it may not import correctly",
"disc:2": "When importing, only profiles that are not already present on the will be added."
},
"about": "If you found this project useful and want to help keep it alive, please to help cover the cost of development and maintenance! Thank you for your support!\n\nPlease feel free to join the discord community or file an issue if you have any problems or suggestions in using this interface.\n\nDisclaimer: The authors of this project are not responsible for any content generated using this interface.\n\nThis 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,\nspread misinformation and target vulnerable groups. For the full list of restrictions please read the license.\n\nBy using this software, you consent to the terms and conditions of the license.\n"
}

View File

@ -1,108 +0,0 @@
{
"title": "Stable Diffusion UI",
"description": "",
"navbar": {
"home": "Home",
"history": "History",
"community": "Community",
"settings": "Settings"
},
"land-cre": {
"cp": "Create Profile",
"cp-place": "Profile name",
"pp": "Profile Picture",
"pp-disc": "",
"ast": "Automatically save to",
"ast-disc": "File path to auto save your creations",
"place": "File path",
"cre": "Create"
},
"land-pre": {
"user": "Username",
"add": "Add Profile"
},
"home": {
"status-starting": "Stable Diffusion is starting...",
"status-ready": "Stable Diffusion is ready to use!",
"status-error": "Stable Diffusion is not running!",
"editor-title": "Prompt",
"initial-img-txt": "Initial Image: (optional)",
"initial-img-btn": "Browse...",
"initial-img-text2": "No file selected.",
"make-img-btn": "Make Image",
"make-img-btn-stop": "Stop"
},
"in-paint": {
"txt": "In-Painting (select the area which the AI will paint into)",
"clear": "Clear"
},
"settings": {
"base-img": "Use base image:",
"seed": "Seed:",
"amount-of-img": "Amount of images to make:",
"how-many": "How many at once:",
"width": "Width:",
"height": "Height:",
"steps": "Number of inference steps:",
"guide-scale": "Guidance Scale:",
"prompt-str": "Prompt Strength:",
"live-preview": "Show a live preview of the image (disable this for faster image generation)",
"fix-face": "Fix incorrect faces and eyes (uses GFPGAN)",
"ups": "Upscale the image to 4x resolution using:",
"no-ups": "No Upscaling",
"corrected": "Show only the corrected/upscaled image"
},
"tags": {
"txt": "Image Modifiers (art styles, tags etc)"
},
"preview-prompt": {
"part1": "Type a prompt and press the \"Make Image\" button.",
"part2": "You can set an \"Initial Image\" if you want to guide the AI.\n",
"part3": "You can also add modifiers like \"Realistic\", \"Pencil Sketch\", \"ArtStation\" etc by browsing through the \"Image Modifiers\" section and selecting the desired modifiers.\n",
"part4": "Click \"Advanced Settings\" for additional settings like seed, image size, number of images to generate etc.",
"part5": "Enjoy! :)"
},
"current-task": "Current task",
"recent-create": "Recently Created",
"popup": {
"use-btn": "Use Image",
"use-btn2": "Use Image and Tags"
},
"history": {
"fave": "Favorites Only",
"search": "Search"
},
"advanced-settings": {
"sound": "Play sound on task completion",
"sound-disc": "Will play a sound so user can hear when image is done.",
"turbo": "Turbo mode",
"turbo-disc": "Generates images faster, but uses an additional 1 GB of GPU memory",
"cpu": "Use CPU instead of GPU",
"cpu-disc": "Warning: this will be *very* slow",
"gpu": "Use full precision",
"gpu-disc": "(for GPU-only. warning: this will consume more VRAM)",
"beta": "Beta Features",
"beta-disc": "Get the latest features immediately (but could be less stable). \nPlease restart the program after changing this.",
"save": "SAVE"
},
"storage": {
"ast": "Automatically save to",
"ast-disc": "File path to auto save your creations",
"place": "File path",
"cps": "Cross profile sharing",
"cps-disc": "Profiles will see suggestions from each other.",
"acb": "Allow cloud backup",
"acb-disc": "A button will show up for images on hover",
"acb-place": "Choose your",
"acc-api": "Api key",
"acb-api-place": "Your API key",
"save": "SAVE"
},
"import": {
"imp-btn": "IMPORT",
"exp-btn": "EXPORT",
"disc": "It is a good idea to leave the exported file as it is. Otherwise it may not import correctly",
"disc:2": "When importing, only profiles that are not already present on the will be added."
},
"about": "If you found this project useful and want to help keep it alive, please to help cover the cost of development and maintenance! Thank you for your support!\n\nPlease feel free to join the discord community or file an issue if you have any problems or suggestions in using this interface.\n\nDisclaimer: The authors of this project are not responsible for any content generated using this interface.\n\nThis 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,\nspread misinformation and target vulnerable groups. For the full list of restrictions please read the license.\n\nBy using this software, you consent to the terms and conditions of the license.\n"
}

View File

@ -1,146 +0,0 @@
/**
* basic server health
*/
import type { SAMPLER_OPTIONS } from "../stores/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
export 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`;
const 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];
};
export const KEY_CONFIG = "config";
export const getConfig = async () => {
const response = await fetch(`${API_URL}/app_config`);
const data = await response.json();
return data;
};
export const KEY_TOGGLE_CONFIG = "toggle_config";
export const toggleBetaConfig = async (branch: string) => {
const response = await fetch(`${API_URL}/app_config`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
update_branch: branch,
}),
});
const data = await response.json();
return data;
};
/**
* post a new request for an image
*/
// TODO; put hese some place better
export interface ImageRequest {
session_id: string;
prompt: string;
negative_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;
mask: undefined | string;
sampler: typeof SAMPLER_OPTIONS[number];
stream_progress_updates: true;
stream_image_progress: boolean;
}
export interface ImageOutput {
data: string;
path_abs: string | null;
seed: number;
}
export interface ImageReturnType {
output: ImageOutput[];
request: ImageRequest;
status: string;
}
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),
});
return res;
};
export const doStopImage = async () => {
const response = await fetch(`${API_URL}/image/stop`);
const data = await response.json();
return data;
};

View File

@ -1,140 +0,0 @@
// would prefer to use a var here, but it doesn't work
// vars: {
// '--button-base-saturation': vars.colorMod.saturation.normal,
// '--button-base-lightness': vars.colorMod.lightness.normal,
// },
import { recipe } from "@vanilla-extract/recipes";
import { vars } from "../../styles/theme/index.css";
export const buttonStyle = recipe({
base: {
fontSize: vars.fonts.sizes.Subheadline,
fontWeight: "bold",
color: vars.colors.text.normal,
padding: vars.spacing.small,
border: "0",
borderRadius: vars.trim.smallBorderRadius,
},
variants: {
color: {
primary: {
// @ts-expect-error
'--button-hue': vars.brandHue,
'--button-base-saturation': vars.colorMod.saturation.normal,
'--button-base-lightness': vars.colorMod.lightness.normal,
},
secondary: {
// @ts-expect-error
'--button-hue': vars.secondaryHue,
'--button-base-saturation': vars.colorMod.saturation.normal,
'--button-base-lightness': vars.colorMod.lightness.normal,
},
tertiary: {
// @ts-expect-error
'--button-hue': vars.tertiaryHue,
'--button-base-saturation': vars.colorMod.saturation.normal,
'--button-base-lightness': vars.colorMod.lightness.normal,
},
cancel: {
// @ts-expect-error
'--button-hue': vars.errorHue,
'--button-base-saturation': vars.colorMod.saturation.normal,
'--button-base-lightness': vars.colorMod.lightness.normal,
},
accent: {
// @ts-expect-error
'--button-hue': vars.backgroundAccentHue,
'--button-base-saturation': vars.backgroundAccentSaturation,
'--button-base-lightness': vars.backgroundAccentLightness,
},
clear: {
backgroundColor: "transparent",
},
},
type: {
fill: {
backgroundColor: `hsl(var(--button-hue),var(--button-base-saturation),${vars.colorMod.lightness.normal})`,
border: `1px solid hsl(var(--button-hue),var(--button-base-saturation),${vars.colorMod.lightness.normal})`,
":hover": {
backgroundColor: `hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.normal})`,
border: `1px solid hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.normal})`,
},
":active": {
backgroundColor: `hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.dim})`,
border: `1px solid hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.dim})`,
},
":focus": {
backgroundColor: `hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.dim})`,
border: `1px solid hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.dim})`,
},
":disabled": {
backgroundColor: `hsl(var(--button-hue),${vars.colorMod.saturation.dim},${vars.colorMod.lightness.dim})`,
border: `1px solid hsl(var(--button-hue),${vars.colorMod.saturation.dim},${vars.colorMod.lightness.dim})`,
},
},
outline: {
backgroundColor: "transparent",
border: `1px solid hsl(var(--button-hue),var(--button-base-saturation),${vars.colorMod.lightness.normal})`,
":hover": {
borderColor: `hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.normal})`,
},
":active": {
borderColor: `hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.dim})`,
},
":focus": {
borderColor: `hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.dim})`,
},
":disabled": {
borderColor: `hsl(var(--button-hue),${vars.colorMod.saturation.dim},${vars.colorMod.lightness.dim})`,
},
},
action: {
backgroundColor: "transparent",
color: `hsl(var(--button-hue),var(--button-base-saturation),${vars.colorMod.lightness.normal})`,
textDecoration: "underline",
":hover": {
color: `hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.normal})`,
},
":active": {
color: `hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.dim})`,
},
":focus": {
color: `hsl(var(--button-hue),${vars.colorMod.saturation.bright},${vars.colorMod.lightness.dim})`,
},
":disabled": {
color: `hsl(var(--button-hue),${vars.colorMod.saturation.dim},${vars.colorMod.lightness.dim})`,
},
}
},
size: {
slim: {
padding: vars.spacing.min,
fontSize: vars.fonts.sizes.Caption,
},
large: {
width: "100%",
fontSize: vars.fonts.sizes.Headline,
}
}
},
defaultVariants: {
color: "primary",
type: "fill",
},
});

View File

@ -1,51 +0,0 @@
import { recipe } from "@vanilla-extract/recipes";
import { vars } from "../../styles/theme/index.css";
export const card = recipe({
base: {
color: vars.colors.text.normal,
padding: vars.spacing.medium,
},
variants: {
backing: {
normal: {
background: vars.backgroundMain,
},
light: {
background: vars.backgroundLight,
},
dark: {
background: vars.backgroundDark,
},
},
rounded: {
true: {
borderRadius: vars.trim.smallBorderRadius,
},
},
info: {
true: {
background: vars.backgroundDark,
border: `1px solid ${vars.backgroundAccentMain}`,
},
},
level: {
flat: {},
1: { boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15)" },
2: { boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15)" },
3: { boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15)" },
},
},
defaultVariants: {
backing: "light",
level: 'flat',
rounded: true,
},
});

View File

@ -1,24 +0,0 @@
import { recipe } from "@vanilla-extract/recipes";
import { vars } from "../../styles/theme/index.css";
export const card = recipe({
base: {
background: vars.backgroundMain,
color: vars.colors.text.normal,
padding: vars.spacing.medium,
borderRadius: vars.trim.smallBorderRadius,
},
variants: {
level: {
1: { boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15)" },
2: { boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15)" },
3: { boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15)" },
},
},
defaultVariants: {
level: 1,
},
});

View File

@ -1,39 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
// import { recipe } from "@vanilla-extract/recipes";
import { vars } from "../../styles/theme/index.css";
import {
card
} from "./card.css";
export const PopoverMain = style({
position: 'relative'
});
export const PopoverButtonStyle = style({
backgroundColor: "transparent",
border: "0 none",
cursor: "pointer",
padding: vars.spacing.none,
fontSize: vars.fonts.sizes.Subheadline,
});
globalStyle(`${PopoverButtonStyle} > i`, {
marginRight: vars.spacing.small,
});
export const PopoverPanelMain = style([card(
{
backing: 'dark',
level: 2,
}
), {
position: 'absolute',
top: '100%',
right: '0',
zIndex: '1',
}]);

View File

@ -1,19 +0,0 @@
import { style, } from "@vanilla-extract/css";
// import { recipe } from "@vanilla-extract/recipes";
import { vars } from "../../styles/theme/index.css";
export const SwitchGroupMain = style({
});
export const SwitchMain = style({
});
export const SwitchLabel = style({
});
export const SwitchEnabled = style({
});
export const SwitchPill = style({
});

View File

@ -1,60 +0,0 @@
import { style } from '@vanilla-extract/css';
import { recipe } from "@vanilla-extract/recipes";
import { vars } from "../../styles/theme/index.css";
export const tabStyles = recipe({
base: {
background: vars.backgroundMain,
color: vars.colors.text.normal,
padding: vars.spacing.small,
borderRadius: `${vars.trim.smallBorderRadius} ${vars.trim.smallBorderRadius} 0 0`,
border: `1px solid ${vars.backgroundLight}`,
borderBottom: 'none',
marginLeft: vars.spacing.small,
boxShadow: `0px -1px 4px -2px ${vars.backgroundAccentMain} inset`,
width: 'fit-content',
':focus': {
outline: 'none',
},
},
variants: {
selected: {
true: {
background: vars.backgroundLight,
color: vars.colors.text.normal,
boxShadow: `0px -1px 4px -2px ${vars.backgroundDark} inset`,
}
}
}
});
export const tabPanelStyles = recipe({
base: {
color: vars.colors.text.normal,
// borderRadius: `0 0 ${vars.trim.smallBorderRadius} ${vars.trim.smallBorderRadius}`,
background: vars.backgroundLight,
padding: vars.spacing.medium,
flexGrow: 1,
overflow: 'auto',
// "::-webkit-scrollbar": {
// width: "4px",
// },
},
variants: {
backing: {
normal: {
background: vars.backgroundMain,
},
light: {
background: vars.backgroundLight,
},
dark: {
background: vars.backgroundDark,
},
},
},
});

View File

@ -1,34 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../styles/theme/index.css";
import {
tabPanelStyles,
} from "../../_recipes/tabs_headless.css";
export const TabpanelScrollFlop = style([tabPanelStyles(), {
direction: 'rtl',
// position: 'relative',
overflow: 'overlay',
"::-webkit-scrollbar": {
position: 'absolute',
width: "6px",
backgroundColor: vars.backgroundAccentMain,
},
"::-webkit-scrollbar-thumb": {
backgroundColor: vars.backgroundDark,
borderRadius: "4px",
},
// "::-webkit-scrollbar-button: {
// backgroundColor: vars.backgroundDark,
// }
}]);
globalStyle(`${TabpanelScrollFlop} > *`, {
direction: 'ltr',
});

View File

@ -1,61 +0,0 @@
import React, { Fragment } from "react";
import { Tab } from '@headlessui/react';
import CreationPanel from "../../organisms/creationPanel";
import QueueDisplay from "../../organisms/queueDisplay";
import ProcessingStatus from "../../molecules/queueStatusTab";
import {
tabStyles,
} from "../../_recipes/tabs_headless.css";
import {
TabpanelScrollFlop
} from "./creationTabs.css";
export default function CreationTabs() {
return (
<Tab.Group>
<Tab.List>
<Tab as={Fragment}>
{({ selected }) => (
<button
className={tabStyles({
selected,
})}
>
Create
</button>
)}
</Tab>
<Tab as={Fragment}>
{({ selected }) => (
<button
className={tabStyles({
selected,
})}
>
<ProcessingStatus></ProcessingStatus>
</button>
)}
</Tab>
</Tab.List>
<Tab.Panels className={TabpanelScrollFlop}>
<Tab.Panel>
<CreationPanel></CreationPanel>
</Tab.Panel>
<Tab.Panel>
<QueueDisplay></QueueDisplay>
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
);
}

View File

@ -1,107 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../styles/theme/index.css";
import {
tabStyles
} from "../../_recipes/tabs_headless.css";
export const basicDisplayLayout = style({
width: "100%",
height: "100%",
display: "grid",
gridTemplateColumns: "1fr 250px",
gridTemplateRows: "1fr 250px",
gridTemplateAreas: `
"content info"
"history history"`,
overflow: "hidden",
selectors: {
'&[data-hide-info]': {
gridTemplateColumns: "1fr",
gridTemplateRows: "1fr 250px",
// gridTemplateAreas: `
// "content"
// "history"`,
},
'&[data-hide-history]': {
gridTemplateColumns: "1fr 250px",
gridTemplateRows: "1fr 0px",
// gridTemplateAreas: `
// "content info"`,
},
'&[data-hide-info][data-hide-history]': {
gridTemplateColumns: "1fr 0px",
gridTemplateRows: "1fr 0px",
// gridTemplateAreas: `
// "content"`,
},
},
// "@media": {
// "screen and (max-width: 800px)": {
// gridTemplateColumns: "1fr",
// gridTemplateRows: "100px 300px 1fr",
// gridTemplateAreas: `
// "header"
// "create"
// "display"
// `,
// },
// },
});
// globalStyle(`${basicDisplayLayout}.hideHistory`, {
// });
export const contentLayout = style({
gridArea: "content",
});
export const infoLayout = style({
gridArea: "info",
position: "relative",
});
export const historyLayout = style({
gridArea: "history",
position: "relative",
});
globalStyle(`${historyLayout} > button`, {
position: "absolute",
top: '-29px',
});
export const displayContainer = style({
flexGrow: 1,
overflow: 'auto',
display: "flex",
});
export const displayData = style({
width: '250px',
height: '100%',
backgroundColor: 'rebeccapurple',
position: 'relative',
});
export const DataTab = style([tabStyles(), {
position: 'absolute',
top: '0',
left: '0',
// pretty sure this is a magic number
transformOrigin: '37% 275%',
transform: 'rotate(-90deg)',
}]);
// export const previousImages = style({
// minHeight: '250px',
// });

View File

@ -1,65 +0,0 @@
import React, { useState, useRef, useEffect } from "react";
import { Transition } from '@headlessui/react'
import CurrentDisplay from "../../organisms/currentDisplay";
import CompletedImages from "../../organisms/completedImages";
import CurrentInfo from "../../organisms/currentInfo";
import {
tabStyles
} from "../../_recipes/tabs_headless.css";
import {
basicDisplayLayout,
contentLayout,
infoLayout,
historyLayout
} from "./basicDisplay.css";
export default function BasicDisplay() {
const [isShowingHistory, setIsShowingHistory] = useState(true)
const layoutRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (layoutRef.current != undefined) {
// set the hide-history data attribute
if (isShowingHistory) {
layoutRef.current.removeAttribute('data-hide-history');
}
else {
// layoutRef.current.dataset.hideHistory = "true";
layoutRef.current.setAttribute('data-hide-history', '');
}
}
}, [layoutRef, isShowingHistory]);
return (
<div
ref={layoutRef}
className={basicDisplayLayout}
>
<div className={contentLayout}>
<CurrentDisplay></CurrentDisplay>
</div>
{/* <div className={infoLayout}>
<CurrentInfo ></CurrentInfo>
</div> */}
<div className={historyLayout}>
<button
className={tabStyles({})}
onClick={() => setIsShowingHistory((isShowingHistory) => !isShowingHistory)}>
{isShowingHistory ? "Hide History" : "Show History"}
</button>
<CompletedImages></CompletedImages>
</div>
</div>
);
}

View File

@ -1,16 +0,0 @@
import React from "react";
import { API_URL } from "../../../api";
const url = `${API_URL}/ding.mp3`;
const AudioDing = React.forwardRef((props, ref) => (
// @ts-expect-error
<audio ref={ref} style={{ display: "none" }}>
<source src={url} type="audio/mp3" />
</audio>
));
AudioDing.displayName = "AudioDing";
export default AudioDing;

View File

@ -1,73 +0,0 @@
import React, { useState, useEffect } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import {
KEY_CONFIG,
getConfig,
KEY_TOGGLE_CONFIG,
toggleBetaConfig,
} from "../../../api";
import { useTranslation } from "react-i18next";
export default function BetaMode() {
const { t } = useTranslation();
// gate for the toggle
const [shouldSetCofig, setShouldSetConfig] = useState(false);
// next branch to get
const [branchToGetNext, setBranchToGetNext] = useState("beta");
// our basic config
const { status: configStatus, data: configData } = useQuery(
[KEY_CONFIG],
getConfig
);
const queryClient = useQueryClient();
// the toggle config
const { status: toggleStatus, data: toggleData } = useQuery(
[KEY_TOGGLE_CONFIG],
async () => await toggleBetaConfig(branchToGetNext),
{
enabled: shouldSetCofig,
}
);
// this is also in the Header Display
// TODO: make this a custom hook
useEffect(() => {
if (configStatus === "success") {
const { update_branch: updateBranch } = configData;
if (updateBranch === "main") {
setBranchToGetNext("beta");
} else {
// setIsBeta(true);
setBranchToGetNext("main");
}
}
}, [configStatus, configData]);
useEffect(() => {
if (toggleStatus === "success") {
if (toggleData[0] === "OK") {
// force a refetch of the config
void queryClient.invalidateQueries([KEY_CONFIG])
}
setShouldSetConfig(false);
}
}, [toggleStatus, toggleData, setShouldSetConfig]);
return (
<label>
<input
type="checkbox"
checked={branchToGetNext === "main"}
onChange={(e) => {
setShouldSetConfig(true);
}}
/>🔥
{t("advanced-settings.beta")} {t("advanced-settings.beta-disc")}
</label>
);
}

View File

@ -1,35 +0,0 @@
import React from "react";
import { doStopImage } from "../../../api";
import { useRequestQueue } from "../../../stores/requestQueueStore";
import {
buttonStyle
} from "../../_recipes/button.css";
export default function ClearQueue() {
const hasQueue = useRequestQueue((state) => state.hasAnyQueue());
const clearQueue = useRequestQueue((state) => state.clearQueue);
const stopAll = async () => {
try {
clearQueue();
const res = await doStopImage();
} catch (e) {
console.log(e);
}
};
return (
<button className={buttonStyle(
{
color: "cancel",
size: "large",
}
)}
disabled={!hasQueue} onClick={() => void stopAll()}>
STOP ALL
</button>
);
}

View File

@ -1,22 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
export const DrawImageMain = style({
position: "relative",
});
globalStyle(`${DrawImageMain} > canvas`, {
position: "absolute",
top: "0",
left: "0",
width: "100%",
height: "100%",
});
globalStyle(`${DrawImageMain} > canvas:first-of-type`, {
opacity: ".7",
});
globalStyle(`${DrawImageMain} > img`, {
top: "0",
left: "0",
});

View File

@ -1,194 +0,0 @@
import React, { useRef, useState, useEffect } from "react";
import {
DrawImageMain,
} from "./drawImage.css";
// https://github.com/embiem/react-canvas-draw
interface DrawImageProps {
imageData: string;
brushSize: number;
brushShape: string;
brushColor: string;
isErasing: boolean;
setData: (data: string) => void;
}
export default function DrawImage({
imageData,
brushSize,
brushShape,
brushColor,
isErasing,
setData,
}: DrawImageProps) {
const drawingRef = useRef<HTMLCanvasElement>(null);
const cursorRef = useRef<HTMLCanvasElement>(null);
const [isUpdating, setIsUpdating] = useState(false);
const [canvasWidth, setCanvasWidth] = useState(512);
const [canvasHeight, setCanvasHeight] = useState(512);
useEffect(() => {
const img = new Image();
img.onload = () => {
setCanvasWidth(img.width);
setCanvasHeight(img.height);
};
img.src = imageData;
}, [imageData]);
useEffect(() => {
// when the brush color changes, change the color of all the
// drawn pixels to the new color
if (drawingRef.current != null) {
const ctx = drawingRef.current.getContext("2d");
if (ctx != null) {
const imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
if (data[i + 3] > 0) {
data[i] = parseInt(brushColor, 16);
data[i + 1] = parseInt(brushColor, 16);
data[i + 2] = parseInt(brushColor, 16);
}
}
ctx.putImageData(imageData, 0, 0);
}
}
}, [brushColor]);
const _handleMouseDown = (
e: React.MouseEvent<HTMLCanvasElement, MouseEvent>
) => {
const {
nativeEvent: { offsetX, offsetY },
} = e;
setIsUpdating(true);
};
const _handleMouseUp = (
e: React.MouseEvent<HTMLCanvasElement, MouseEvent>
) => {
setIsUpdating(false);
const canvas = drawingRef.current;
if (canvas != null) {
const data = canvas.toDataURL();
setData(data);
}
};
const _drawCanvas = (x: number, y: number, brushSize: number, brushShape: string, brushColor: string | CanvasGradient | CanvasPattern) => {
const canvas = drawingRef.current;
if (canvas != null) {
const ctx = canvas.getContext("2d");
if (ctx != null) {
if (isErasing) {
// stack overflow https://stackoverflow.com/questions/10396991/clearing-circular-regions-from-html5-canvas
const offset = brushSize / 2;
ctx.clearRect(x - offset, y - offset, brushSize, brushSize);
} else {
ctx.beginPath();
ctx.lineWidth = brushSize;
// @ts-expect-error
ctx.lineCap = brushShape;
ctx.strokeStyle = brushColor;
ctx.moveTo(x, y);
ctx.lineTo(x, y);
ctx.stroke();
}
}
}
};
const _drawCursor = (
x: number,
y: number,
brushSize: number,
brushShape: string,
brushColor: string
) => {
const canvas = cursorRef.current;
if (canvas != null) {
const ctx = canvas.getContext("2d");
if (ctx != null) {
ctx.beginPath();
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (isErasing) {
const offset = brushSize / 2;
// draw a quare
ctx.lineWidth = 2;
ctx.lineCap = "butt";
ctx.strokeStyle = brushColor;
ctx.moveTo(x - offset, y - offset);
ctx.lineTo(x + offset, y - offset);
ctx.lineTo(x + offset, y + offset);
ctx.lineTo(x - offset, y + offset);
ctx.lineTo(x - offset, y - offset);
ctx.stroke();
} else {
ctx.lineWidth = brushSize;
// @ts-expect-error
ctx.lineCap = brushShape;
ctx.strokeStyle = brushColor;
ctx.moveTo(x, y);
ctx.lineTo(x, y);
ctx.stroke();
}
}
}
};
const _handleMouseMove = (
e: React.MouseEvent<HTMLCanvasElement, MouseEvent>
) => {
const {
nativeEvent: { offsetX: x, offsetY: y },
} = e;
_drawCursor(x, y, brushSize, brushShape, brushColor);
if (isUpdating) {
_drawCanvas(x, y, brushSize, brushShape, brushColor);
}
};
// function for external use
const fillCanvas = () => {
const canvas = drawingRef.current;
if (canvas != null) {
const ctx = canvas.getContext("2d");
if (ctx != null) {
ctx.fillStyle = brushColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
}
};
return (
<div className={DrawImageMain}>
<img src={imageData} />
<canvas
ref={drawingRef}
width={canvasWidth}
height={canvasHeight}
></canvas>
<canvas
ref={cursorRef}
width={canvasWidth}
height={canvasHeight}
onMouseDown={_handleMouseDown}
onMouseUp={_handleMouseUp}
onMouseMove={_handleMouseMove}
></canvas>
</div>
);
}

View File

@ -1,11 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
export const generatedImageMain = style({
position: "relative",
});
globalStyle(`${generatedImageMain} img`, {
width: "100%",
height: "100%",
objectFit: "contain",
});

View File

@ -1,25 +0,0 @@
import React from "react";
import { ImageRequest } from "../../../api";
import {
generatedImageMain,
} from "./generatedImage.css";
interface GeneretaedImageProps {
imageData: string | undefined;
metadata: ImageRequest | undefined;
className?: string;
}
export default function GeneratedImage({
imageData,
metadata,
className,
}: GeneretaedImageProps) {
return (
<div className={[generatedImageMain, className].join(" ")}>
<img src={imageData} alt={metadata!.prompt} />
</div>
);
}

View File

@ -1,276 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import React, { useEffect, useRef } from "react";
import { useImageCreate } from "../../../stores/imageCreateStore";
import {
QueueStatus,
useRequestQueue
} from "../../../stores/requestQueueStore";
import {
FetchingStates,
useImageFetching
} from "../../../stores/imageFetchingStore";
import { useImageDisplay } from "../../../stores/imageDisplayStore";
import { v4 as uuidv4 } from "uuid";
import { useRandomSeed } from "../../../utils";
import {
ImageRequest,
ImageReturnType,
ImageOutput,
doMakeImage,
} from "../../../api";
import {
buttonStyle
} from "../../_recipes/button.css";
import { useTranslation } from "react-i18next";
import AudioDing from "../../molecules/audioDing";
const idDelim = "_batch";
export default function MakeButton() {
const { t } = useTranslation();
const dingRef = useRef<HTMLAudioElement>();
const parallelCount = useImageCreate((state) => state.parallelCount);
const builtRequest = useImageCreate((state) => state.builtRequest);
const isRandomSeed = useImageCreate((state) => state.isRandomSeed());
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const isSoundEnabled = useImageCreate((state) => state.isSoundEnabled());
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);
const setStep = useImageFetching((state) => state.setStep);
const setTotalSteps = useImageFetching((state) => state.setTotalSteps);
const addProgressImage = useImageFetching((state) => state.addProgressImage);
const setStartTime = useImageFetching((state) => state.setStartTime);
const setNowTime = useImageFetching((state) => state.setNowTime);
const resetForFetching = useImageFetching((state) => state.resetForFetching);
const appendData = useImageFetching((state) => state.appendData);
const updateDisplay = useImageDisplay((state) => state.updateDisplay);
const hackJson = (jsonStr: string, id: string) => {
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;
const seedReq = {
...request,
seed,
};
const batchId = `${id}${idDelim}-${seed}-${index}`;
updateDisplay(batchId, data, seedReq);
});
}
else {
console.warn(`Unexpected status: ${status}`);
updateQueueStatus(id, QueueStatus.error);
}
}
catch (e) {
updateQueueStatus(id, QueueStatus.error);
console.warn("Error HACKING JSON: ", e)
}
}
const parseRequest = async (id: string, reader: ReadableStreamDefaultReader<Uint8Array>) => {
const decoder = new TextDecoder();
let finalJSON = '';
while (true) {
const { done, value } = await reader.read();
const jsonStr = decoder.decode(value);
if (done) {
setStatus(FetchingStates.COMPLETE);
hackJson(finalJSON, id);
if (isSoundEnabled) {
void dingRef.current?.play();
}
break;
}
try {
const update = JSON.parse(jsonStr);
const { status } = update;
if (status === "progress") {
setStatus(FetchingStates.PROGRESSING);
const { progress: { step, total_steps }, output: outputs } = update;
setStep(step);
setTotalSteps(total_steps);
if (step === 0) {
setStartTime();
}
else {
setNowTime();
}
if (void 0 !== outputs) {
outputs.forEach((output: any) => {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
const timePath = `${output.path}?t=${new Date().getTime()}`
addProgressImage(timePath);
});
}
} else if (status === "succeeded") {
// TODO this should be the the new out instead of the try catch
// wait for the path to come back instead of the data
setStatus(FetchingStates.SUCCEEDED);
}
else if (status === 'failed') {
console.warn('failed');
console.warn(update);
}
else {
console.warn("UNKNOWN ?", update);
}
}
catch (e) {
// console.log('EXPECTED PARSE ERRROR')
finalJSON += jsonStr;
}
}
}
const startStream = async (id: string, req: ImageRequest) => {
try {
updateQueueStatus(id, QueueStatus.processing);
resetForFetching();
const res = await doMakeImage(req);
const reader = res.body?.getReader();
if (void 0 !== reader) {
void parseRequest(id, reader);
}
} catch (e) {
console.log('TOP LINE STREAM ERROR')
updateQueueStatus(id, QueueStatus.error);
console.log(e);
}
}
const queueImageRequest = (req: ImageRequest) => {
// the actual number of request we will make
const requests = [];
// the number of images we will make
let { num_outputs } = req;
if (parallelCount > num_outputs) {
requests.push(num_outputs);
} else {
// while we have at least 1 image to make
while (num_outputs >= 1) {
// subtract the parallel count from the number of images to make
num_outputs -= parallelCount;
// if we are still 0 or greater we can make the full parallel count
if (num_outputs <= 0) {
requests.push(parallelCount);
}
// otherwise we can only make the remaining images
else {
requests.push(Math.abs(num_outputs));
}
}
}
requests.forEach((num, index) => {
// get the seed we want to use
let seed = req.seed;
if (index !== 0) {
// we want to use a random seed for subsequent requests
seed = useRandomSeed();
}
// add the request to the queue
addtoQueue(uuidv4(), {
...req,
// updated the number of images to make
num_outputs: num,
// update the seed
seed,
});
});
}
const makeImageQueue = async () => {
// potentially update the seed
if (isRandomSeed) {
// update the seed for the next time we click the button
setRequestOption("seed", useRandomSeed());
}
// the request that we have built
const req = builtRequest();
queueImageRequest(req);
};
useEffect(() => {
const makeImages = async (options: ImageRequest) => {
// removeFirstInQueue();
await startStream(id ?? "", options);
}
if (status === FetchingStates.PROGRESSING || status === FetchingStates.FETCHING) {
return;
}
if (hasQueue) {
if (options === undefined) {
console.log('req is undefined');
return;
}
makeImages(options).catch((e) => {
console.log('HAS QUEUE ERROR');
console.log(e);
});
}
}, [hasQueue, status, id, options, startStream]);
return (
<>
<button
className={buttonStyle({
size: "large",
})}
onClick={() => {
void makeImageQueue();
}}
>
{t("home.make-img-btn")}
</button>
<AudioDing ref={dingRef}></AudioDing>
</>
);
}

View File

@ -1,93 +0,0 @@
import React, { useState } from "react";
import { v4 as uuidv4 } from "uuid";
import {
ModifierPreview,
useImageCreate
} from "../../../stores/imageCreateStore";
import { API_URL } from "../../../api";
import {
IconFont,
} from "../../../styles/shared.css";
import {
ModifierTagMain,
ModifierActions,
tagPreview,
TagText,
TagToggle,
} from "./modifierTags.css";
interface ModifierTagProps {
name: string;
category: string;
previews: ModifierPreview[];
}
export default function ModifierTag({ name, category, previews }: ModifierTagProps) {
const previewType: 'portrait' | 'landscape' = "portrait";
const [showActions, setShowActions] = useState(false);
const handleHover = () => {
setShowActions(true);
};
const handleLeave = () => {
setShowActions(false);
};
const addCreateTag = useImageCreate((state) => state.addCreateTag);
const setPositivePrompt = () => {
addCreateTag({ id: uuidv4(), name, type: 'positive' });
}
const setNegativePrompt = () => {
addCreateTag({ id: uuidv4(), name, type: 'negative' });
}
const hasTag = useImageCreate((state) => state.hasTag(category, name))
? "selected"
: "";
const toggleTag = useImageCreate((state) => state.toggleTag);
const _toggleTag = () => {
toggleTag(category, name);
};
// , hasTag].join(" ")
return (
<div className={ModifierTagMain}
onMouseEnter={handleHover}
onMouseLeave={handleLeave}>
<p className={!showActions ? TagText : TagToggle}>{name}</p>
{showActions && (
<div className={ModifierActions}>
<button onClick={setPositivePrompt}>
<i className={[IconFont, 'fa-solid', 'fa-plus'].join(" ")}></i>
</button>
<button onClick={setNegativePrompt}>
<i className={[IconFont, 'fa-solid', 'fa-minus'].join(" ")}></i>
</button>
</div>
)}
{/* <div className={tagPreview}>
{previews.map((preview) => {
if (preview.name !== previewType) {
return null;
}
return (
<img
key={preview.name}
src={`${API_URL}/media/modifier-thumbnails/${preview.path}`}
alt={preview.name}
title={preview.name}
/>
);
})}
</div> */}
</div>
);
}

View File

@ -1,69 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from '../../../styles/theme/index.css';
import { card } from '../../_recipes/card.css';
export const ModifierTagMain = style([
card({
backing: 'normal',
level: 1,
info: true
}), {
position: "relative",
width: "fit-content",
borderColor: `hsl(${vars.brandHue}, ${vars.colorMod.saturation.normal}, ${vars.colorMod.lightness.normal})`,
padding: vars.spacing.small,
}
]);
globalStyle(`${ModifierTagMain}.selected`, {
backgroundColor: "rgb(131, 11, 121)",
})
globalStyle(`${ModifierTagMain} p`, {
margin: 0,
textAlign: "center",
marginBottom: "2px",
});
export const TagText = style({
opacity: 1,
});
export const TagToggle = style({
opacity: 0.3,
});
export const ModifierActions = style({
position: "absolute",
top: "0",
left: "0",
height: "100%",
width: "100%",
display: "flex",
flexDirection: "row",
});
globalStyle(`${ModifierActions} button`, {
flexGrow: 1,
backgroundColor: "transparent",
border: "none",
boxShadow: `inset 0 0 24px 0px rgb(255 255 255 / 50%)`,
borderRadius: "5px",
padding: "0",
});
export const tagPreview = style({
display: 'flex',
justifyContent: 'center',
});
globalStyle(`${tagPreview} img`, {
width: "90px",
height: "100%",
objectFit: "cover",
objectPosition: "center",
});

View File

@ -1,68 +0,0 @@
import React, { useState } from "react";
import { useImageCreate } from "../../../stores/imageCreateStore";
import {
IconFont,
} from "../../../styles/shared.css";
import {
PromptTagMain,
TagToggle,
TagRemoveButton,
PromptTagText,
PromptTagToggle
} from "./promptTag.css";
interface PromptTagProps {
id: string;
name: string;
category?: string;
previews?: string[];
type: string;
};
export default function PromptTag({ id, name, category, previews, type }: PromptTagProps) {
const [showToggle, setShowToggle] = useState(false);
const removeCreateTag = useImageCreate((state) => state.removeCreateTag);
const changeCreateTagType = useImageCreate((state) => state.changeCreateTagType);
const handleHover = () => {
setShowToggle(true);
};
const handleLeave = () => {
setShowToggle(false);
};
const toggleType = () => {
if (type === 'positive') {
changeCreateTagType(id, 'negative');
}
else {
changeCreateTagType(id, 'positive');
}
};
const handleRemove = () => {
console.log('remove');
removeCreateTag(id);
};
return (
<div
onMouseEnter={handleHover}
onMouseLeave={handleLeave}
className={[PromptTagMain, type].join(' ')}>
<p className={!showToggle ? PromptTagText : PromptTagToggle}>{name}</p>
{showToggle && <button className={TagToggle} onClick={toggleType}>
{type === 'positive' ? <i className={[IconFont, 'fa-solid', 'fa-minus'].join(" ")}></i> : <i className={[IconFont, 'fa-solid', 'fa-plus'].join(" ")}></i>}
</button>}
{showToggle && <button className={TagRemoveButton} onClick={handleRemove}>
<i className={[IconFont, 'fa-solid', 'fa-close'].join(" ")}></i>
</button>}
</div>
);
};

View File

@ -1,55 +0,0 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import { style, globalStyle } from '@vanilla-extract/css';
import { XButton } from "../../../styles/shared.css";
import { vars } from '../../../styles/theme/index.css';
import { card } from '../../_recipes/card.css';
export const PromptTagMain = style([
card({
backing: 'normal',
level: 1,
info: true
}), {
position: "relative",
width: "fit-content",
backgroundColor: `hsl(${vars.backgroundLight}, ${vars.colorMod.saturation.normal}, ${vars.colorMod.lightness.normal})`,
padding: vars.spacing.small,
}
]);
export const PromptTagText = style({
opacity: 1,
fontSize: vars.fonts.sizes.Plain,
});
export const PromptTagToggle = style({
opacity: 0.3,
fontSize: vars.fonts.sizes.Plain,
});
globalStyle(`${PromptTagMain}.positive`, {
borderColor: `hsl(${vars.brandHue}, ${vars.colorMod.saturation.normal}, ${vars.colorMod.lightness.normal})`,
});
globalStyle(`${PromptTagMain}.negative`, {
borderColor: `hsl(${vars.errorHue}, ${vars.colorMod.saturation.normal}, ${vars.colorMod.lightness.normal})`,
});
export const TagToggle = style({
position: "absolute",
top: "0",
right: "0",
height: "100%",
width: "100%",
border: "none",
backgroundColor: "transparent",
boxShadow: `inset 0 0 24px 0px rgb(255 255 255 / 50%)`,
});
export const TagRemoveButton = style([XButton, {
top: '-4px',
left: '4px',
padding: '0',
}]);

View File

@ -1,70 +0,0 @@
import React, { useEffect, useState } from "react";
import { FetchingStates, useImageFetching } from "../../../stores/imageFetchingStore";
import { useRequestQueue } from "../../../stores/requestQueueStore";
export default function QueueStatusTab() {
const [showBasicQueue, setShowBasicQueue] = useState(true);
const hasPendingQueue = useRequestQueue((state) => state.hasPendingQueue());
const pendingRequests = useRequestQueue((state) => state.pendingRequests());
const status = useImageFetching((state) => state.status);
const step = useImageFetching((state) => state.step);
const totalSteps = useImageFetching((state) => state.totalSteps);
const startTime = useImageFetching((state) => state.timeStarted);
const timeNow = useImageFetching((state) => state.timeNow);
const [timeRemaining, setTimeRemaining] = useState(0);
const [percent, setPercent] = useState(0);
useEffect(() => {
if (totalSteps > 0) {
setPercent(Math.round((step / totalSteps) * 100));
} else {
setPercent(0);
}
}, [step, totalSteps]);
useEffect(() => {
// find the remaining time
const timeTaken = +timeNow - +startTime;
const timePerStep = step == 0 ? 0 : timeTaken / step;
const totalTime = timePerStep * totalSteps;
const timeRemaining = (totalTime - timeTaken) / 1000;
// @ts-expect-error
setTimeRemaining(timeRemaining.toPrecision(3));
}, [step, totalSteps, startTime, timeNow, setTimeRemaining]);
useEffect(() => {
if (hasPendingQueue) {
setShowBasicQueue(false);
}
}, [status, hasPendingQueue]);
// {/* {showBasicQueue
// ? <> */}
// Queue
// {/* </>
// : <>
// <span>Percent: {percent}%</span>
// </>npm
// } */}
return (
<>
<span>Queue </span>
{hasPendingQueue && <span> Items Remaining: {pendingRequests.length} </span>}
</>
)
}

View File

@ -1,25 +0,0 @@
import React from "react";
import { doStopImage } from "../../../api";
import {
buttonStyle
} from "../../_recipes/button.css";
export default function StopButton() {
const stopMake = async () => {
try {
const res = await doStopImage();
} catch (e) {
console.log(e);
}
};
return <button className={buttonStyle(
{
color: "cancel",
size: "large",
}
)} onClick={() => void stopMake()}>Stop</button>;
}

View File

@ -1,63 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../styles/theme/index.css";
import {
card as cardStyle,
} from '../../_recipes/card.css'
export const completedImagesMain = style({
position: "relative",
});
// globalStyle(`${completedImagesMain} > button`, {
// position: "absolute",
// top: '-29px',
// });
export const completedImagesContent = style([cardStyle(), {
height: "250px",
width: "100%",
display: "flex",
padding: vars.spacing.medium,
borderRadius: 0,
}]);
export const completedImagesList = style({
display: "flex",
flexDirection: "row",
flexWrap: "nowrap",
height: "100%",
width: "100%",
overflow: "auto",
paddingLeft: vars.spacing.none,
});
globalStyle(`${completedImagesContent} li`, {
position: "relative",
});
globalStyle(`${completedImagesContent} > li:first-of-type`, {
marginLeft: vars.spacing.medium,
});
globalStyle(`${completedImagesContent} > li:last-of-type`, {
marginRight: 0,
});
export const imageContain = style({
width: "206px",
backgroundColor: "black",
display: "flex",
justifyContent: "center",
alignItems: "center",
flexShrink: 0,
border: "0 none",
padding: "0",
marginLeft: vars.spacing.medium,
cursor: "pointer",
});
globalStyle(`${imageContain} img`, {
width: "100%",
objectFit: "contain",
});

View File

@ -1,87 +0,0 @@
import React, { useState } from "react";
import { useImageDisplay } from "../../../stores/imageDisplayStore";
import {
completedImagesMain,
completedImagesContent,
completedImagesList,
imageContain,
} from "./completedImages.css";
import {
buttonStyle
} from "../../_recipes/button.css";
// import { Transition } from '@headlessui/react'
// import {
// tabStyles
// } from "../../_recipes/tabs_headless.css";
export default function CompletedImages(
) {
const [isShowing, setIsShowing] = useState(false)
const images = useImageDisplay((state) => state.images);
const setCurrentImage = useImageDisplay((state) => state.setCurrentImage);
const clearDisplay = useImageDisplay((state) => state.clearDisplay);
const removeImagesAll = () => {
clearDisplay();
};
return (
<div className={completedImagesMain}>
{/* <button
className={tabStyles({})}
onClick={() => setIsShowing((isShowing) => !isShowing)}>
{isShowing ? "Hide History" : "Show History"}
</button> */}
{/* <Transition
show={isShowing}
> */}
<div className={completedImagesContent}>
{/* Adjust the dom do we dont do this check twice */}
{images != null && images.length > 0 && (
<button
className={buttonStyle()}
onClick={() => {
removeImagesAll();
}}
>
REMOVE ALL
</button>
)}
<ul className={completedImagesList}>
{images?.map((image, index) => {
if (void 0 === image) {
console.warn(`image ${index} is undefined`);
return null;
}
return (
<li key={image.id}>
<button
className={imageContain}
onClick={() => {
setCurrentImage(image);
}}
>
<img src={image.data} alt={image.info.prompt} />
</button>
</li>
);
})}
</ul>
</div>
{/* </Transition> */}
</div>
);
}

View File

@ -1,12 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../../styles/theme/index.css";
export const AdvancedSettingsList = style({
paddingLeft: 0,
listStyleType: "none",
});
export const AdvancedSettingGrouping = style({
marginTop: vars.spacing.small,
});

View File

@ -1,119 +0,0 @@
import React, { useEffect, useState } from "react";
import { useImageCreate } from "../../../../../stores/imageCreateStore";
import { useCreateUI } from "../../creationPanelUIStore";
import {
SettingItem,
} from "../../../../../styles/shared.css";
import {
buttonStyle,
} from "../../../../_recipes/button.css";
import { useTranslation } from "react-i18next";
export default function ImprovementSettings() {
const { t } = useTranslation();
// these are conditionals that should be retired and inferred from the store
const isUsingFaceCorrection = useImageCreate((state) =>
state.isUsingFaceCorrection()
);
const isUsingUpscaling = useImageCreate((state) => state.isUsingUpscaling());
const useUpscale = useImageCreate((state) =>
state.getValueForRequestKey("use_upscale")
);
const filteredOnly = useImageCreate((state) =>
state.getValueForRequestKey("show_only_filtered_image")
);
const toggleUseFaceCorrection = useImageCreate(
(state) => state.toggleUseFaceCorrection
);
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const improvementOpen = useCreateUI(
(state) => state.isOpenAdvImprovementSettings
);
const toggleImprovementOpen = useCreateUI(
(state) => state.toggleAdvImprovementSettings
);
const [isFilteringDisabled, setIsFilteringDisabled] = useState(false);
// should probably be a store selector
useEffect(() => {
// if either are true we arent disabled
if (isUsingFaceCorrection || useUpscale != "") {
setIsFilteringDisabled(false);
} else {
setIsFilteringDisabled(true);
}
}, [isUsingFaceCorrection, isUsingUpscaling, setIsFilteringDisabled]);
return (
<div>
<button
type="button"
className={buttonStyle({
type: 'action',
color: 'accent',
})}
onClick={toggleImprovementOpen}
>
Improvement Settings
</button>
{improvementOpen && (
<>
<div className={SettingItem}>
<label>
<input
type="checkbox"
checked={isUsingFaceCorrection}
onChange={(e) => toggleUseFaceCorrection()}
/>
Fix incorrect faces and eyes (uses GFPGAN)
</label>
</div>
<div className={SettingItem}>
<label>
{t("settings.ups")}
<select
id="upscale_model"
name="upscale_model"
value={useUpscale}
onChange={(e) => {
setRequestOption("use_upscale", e.target.value);
}}
>
<option value="">{t("settings.no-ups")}</option>
<option value="RealESRGAN_x4plus">RealESRGAN_x4plus</option>
<option value="RealESRGAN_x4plus_anime_6B">
RealESRGAN_x4plus_anime_6B
</option>
</select>
</label>
</div>
<div className={SettingItem}>
<label>
<input
disabled={isFilteringDisabled}
type="checkbox"
checked={filteredOnly}
onChange={(e) =>
setRequestOption("show_only_filtered_image", e.target.checked)
}
/>
{t("settings.corrected")}
</label>
</div>
</>
)}
</div>
);
}

View File

@ -1,69 +0,0 @@
import React, { useEffect } from "react";
import { useCreateUI } from "../creationPanelUIStore";
import {
card
} from '../../../_recipes/card.css';
import {
buttonStyle,
} from "../../../_recipes/button.css";
import {
AdvancedSettingsList,
AdvancedSettingGrouping,
} from "./advancedsettings.css";
import ImprovementSettings from "./improvementSettings";
import PropertySettings from "./propertySettings";
import WorkflowSettings from "./workflowSettings";
function SettingsList() {
return (
<ul className={AdvancedSettingsList}>
<li className={AdvancedSettingGrouping}>
<ImprovementSettings />
</li>
<li className={AdvancedSettingGrouping}>
<PropertySettings />
</li>
<li className={AdvancedSettingGrouping}>
<WorkflowSettings />
</li>
</ul>
);
}
export default function AdvancedSettings() {
const advancedSettingsIsOpen = useCreateUI(
(state) => state.isOpenAdvancedSettings
);
const toggleAdvancedSettingsIsOpen = useCreateUI(
(state) => state.toggleAdvancedSettings
);
return (
<div className={card(
{
level: 1,
backing: 'normal'
}
)}>
<button
type="button"
onClick={toggleAdvancedSettingsIsOpen}
className={buttonStyle({
type: 'action',
color: 'secondary',
size: 'large'
})}
>
Advanced Settings
</button>
{advancedSettingsIsOpen && <SettingsList />}
</div >
);
}

View File

@ -1,211 +0,0 @@
import React from "react";
import { useImageCreate, SAMPLER_OPTIONS } from "../../../../../stores/imageCreateStore";
import { useCreateUI } from "../../creationPanelUIStore";
import {
SettingItem,
} from "../../../../../styles/shared.css";
import {
buttonStyle,
} from "../../../../_recipes/button.css";
import { useTranslation } from "react-i18next";
// 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 (*)" },
];
export default function PropertySettings() {
const { t } = useTranslation();
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const toggleUseRandomSeed = useImageCreate(
(state) => state.toggleUseRandomSeed
);
const isRandomSeed = useImageCreate((state) => state.isRandomSeed());
const seed = useImageCreate((state) => state.getValueForRequestKey("seed"));
const steps = useImageCreate((state) =>
state.getValueForRequestKey("num_inference_steps")
);
const guidanceScale = useImageCreate((state) =>
state.getValueForRequestKey("guidance_scale")
);
const initImage = useImageCreate((state) =>
state.getValueForRequestKey("init_image")
);
const promptStrength = useImageCreate((state) =>
state.getValueForRequestKey("prompt_strength")
);
const width = useImageCreate((state) => state.getValueForRequestKey("width"));
const height = useImageCreate((state) =>
state.getValueForRequestKey("height")
);
const sampler = useImageCreate((state) =>
state.getValueForRequestKey("sampler")
);
const propertyOpen = useCreateUI((state) => state.isOpenAdvPropertySettings);
const togglePropertyOpen = useCreateUI(
(state) => state.toggleAdvPropertySettings
);
return (
<div>
<button type="button" className={buttonStyle({
type: 'action',
color: 'accent',
})} onClick={togglePropertyOpen}>
Property Settings
</button>
{propertyOpen && (
<>
<div className={SettingItem}>
<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>
</div>
<div className={SettingItem}>
<label>
{t("settings.steps")}{" "}
<input
value={steps}
onChange={(e) => {
setRequestOption("num_inference_steps", e.target.value);
}}
size={4}
/>
</label>
</div>
<div className={SettingItem}>
<label>
{t("settings.guide-scale")}
<input
value={guidanceScale}
onChange={(e) =>
setRequestOption("guidance_scale", e.target.value)
}
type="range"
min="0"
max="20"
step=".1"
/>
</label>
<span>{guidanceScale}</span>
</div>
{void 0 !== initImage && (
<div className={SettingItem}>
<label>
{t("settings.prompt-str")}{" "}
<input
value={promptStrength}
onChange={(e) =>
setRequestOption("prompt_strength", e.target.value)
}
type="range"
min="0"
max="1"
step=".05"
/>
</label>
<span>{promptStrength}</span>
</div>
)}
<div className={SettingItem}>
<label>
{t("settings.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>
<label>
{t("settings.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>
</div>
<div className={SettingItem}>
<label>
{t("settings.sampler")}
<select
value={sampler}
onChange={(e) => setRequestOption("sampler", e.target.value)}
>
{SAMPLER_OPTIONS.map((sampler) => (
<option key={`sampler-option_${sampler}`} value={sampler}>
{sampler}
</option>
))}
</select>
</label>
</div>
</>
)}
</div>
);
}

View File

@ -1,84 +0,0 @@
import React from "react";
import { useImageCreate } from "../../../../../stores/imageCreateStore";
import { useCreateUI } from "../../creationPanelUIStore";
import {
SettingItem,
} from "../../../../../styles/shared.css";
import {
buttonStyle,
} from "../../../../_recipes/button.css";
import { useTranslation } from "react-i18next";
export default function WorkflowSettings() {
const { t } = useTranslation();
const numOutputs = useImageCreate((state) =>
state.getValueForRequestKey("num_outputs")
);
const parallelCount = useImageCreate((state) => state.parallelCount);
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const setParallelCount = useImageCreate((state) => state.setParallelCount);
const shouldStreamImages = useImageCreate((state) => state.getValueForRequestKey("stream_image_progress"));
const workflowOpen = useCreateUI((state) => state.isOpenAdvWorkflowSettings);
const toggleWorkflowOpen = useCreateUI(
(state) => state.toggleAdvWorkflowSettings
);
return (
<div>
<button type="button" className={buttonStyle({
type: 'action',
color: 'accent',
})} onClick={toggleWorkflowOpen}>
Workflow Settings
</button>
{workflowOpen && (
<>
<div className={SettingItem}>
<label>
{t("settings.amount-of-img")}{" "}
<input
type="number"
value={numOutputs}
onChange={(e) =>
setRequestOption("num_outputs", parseInt(e.target.value, 10))
}
size={4}
/>
</label>
</div>
<div className={SettingItem}>
<label>
{t("settings.how-many")}
<input
type="number"
value={parallelCount}
onChange={(e) => setParallelCount(parseInt(e.target.value, 10))}
size={4}
/>
</label>
</div>
<div className={SettingItem}>
<label>
{t("settings.stream-img")}
<input
type="checkbox"
checked={shouldStreamImages}
onChange={(e) =>
setRequestOption("stream_image_progress", e.target.checked)
}
/>
</label>
</div>
</>
)}
</div>
);
}

View File

@ -1,20 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { card } from "../../../_recipes/card.css";
export const CreationBasicMain = style([
card({
backing: 'normal',
level: 1
}), {
position: "relative",
width: "100%",
}]
);
export const PromptDisplay = style({});
globalStyle(`${CreationBasicMain} > *`, {
marginBottom: '10px'
});

View File

@ -1,37 +0,0 @@
import React, { ChangeEvent } from "react";
import { useImageCreate } from "../../../../stores/imageCreateStore";
import {
CreationBasicMain,
PromptDisplay,
} from "./basicCreation.css";
import MakeButton from "../../../molecules/makeButton";
import PromptCreator from "./promptCreator";
// import CreationActions from "./creationActions";
import SeedImage from "./seedImage";
import ActiveTags from "./promptCreator/activeTags";
import { useTranslation } from "react-i18next";
export default function BasicCreation() {
const { t } = useTranslation();
const promptText = useImageCreate((state) =>
state.getValueForRequestKey("prompt")
);
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const handlePromptChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
setRequestOption("prompt", event.target.value);
};
return (
<div className={CreationBasicMain}>
<MakeButton></MakeButton>
<PromptCreator></PromptCreator>
<SeedImage></SeedImage>
</div>
);
}

View File

@ -1,18 +0,0 @@
import { style } from '@vanilla-extract/css';
import { vars } from '../../../../../../styles/theme/index.css';
export const ActiveTagListMain = style({
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
gap: '10px',
width: '100%',
height: '100%',
overflow: 'visible',
scrollbarWidth: 'none',
msOverflowStyle: 'none',
'::-webkit-scrollbar': {
display: 'none',
},
});

View File

@ -1,39 +0,0 @@
import React from "react";
import { useImageCreate } from "../../../../../../stores/imageCreateStore";
import ModifierTag from "../../../../../molecules/modifierTag";
// import {
// card
// } from '../../../../../_recipes/card.css';
import PromptTag from "../../../../../molecules/promptTag";
import {
ActiveTagListMain
} from "./activeTags.css";
export default function ActiveTags() {
const selectedtags = useImageCreate((state) => state.selectedTags());
const createTags = useImageCreate((state) => state.createTags);
return (
<div>
<ul className={ActiveTagListMain}>
{createTags.map((tag) => {
console.log(tag);
return (
<li key={tag.id}>
{/* @ts-expect-error */}
<PromptTag id={tag.id} name={tag.name} category={tag?.category} previews={tag?.previews} type={tag.type} />
</li>)
}
)}
</ul>
</div>
);
}

View File

@ -1,108 +0,0 @@
import React, { useState, ChangeEvent, KeyboardEventHandler, Fragment } from "react";
import { v4 as uuidv4 } from "uuid";
import { Switch } from '@headlessui/react'
import { useImageCreate } from "../../../../../stores/imageCreateStore";
import ActiveTags from "./activeTags";
import {
IconFont,
} from "../../../../../styles/shared.css";
import {
buttonStyle,
} from "../../../../_recipes/button.css";
import {
PromptCreatorMain,
ToggleGroupMain,
ToggleMain,
ToggleLabel,
ToggleEnabled,
TogglePill,
buttonRow,
} from "./promptCreator.css";
import { useTranslation } from "react-i18next";
import { type } from "os";
interface TagTypeProps {
positive: boolean;
setPositive: (positive: boolean) => void;
};
function TagTypeToggle({ positive, setPositive }: TagTypeProps) {
return (
<Switch.Group as={Fragment}>
<div className={ToggleGroupMain}>
<Switch.Label> Type </Switch.Label>
<Switch className={ToggleMain} checked={positive} onChange={setPositive}>
<span
className={TogglePill}
>
{positive
? <i className={[IconFont, 'fa-solid', 'fa-plus'].join(" ")}></i>
: <i className={[IconFont, 'fa-solid', 'fa-minus'].join(" ")}></i>}
</span>
</Switch>
</div>
</Switch.Group>
);
}
export default function PromptCreator() {
const [positive, setPositive] = useState(true)
const [tagText, setTagText] = useState('An astronaut riding a horse');
const addCreateTag = useImageCreate((state) => state.addCreateTag);
const { t } = useTranslation();
const checkForEnter = (event: KeyboardEventHandler<HTMLInputElement>) => {
// @ts-expect-error
if (event.key === "Enter") {
if (tagText !== '') {
const type = positive ? "positive" : "negative";
tagText.split(',').map((tag) => tag.trim()).forEach((tag) => {
addCreateTag({ id: uuidv4(), name: tag, type });
});
//debugger;
setTagText('');
}
}
};
return (
<div className={PromptCreatorMain}>
<div>
<p>{t("home.editor-title")}</p>
{/* @ts-expect-error */}
<input value={tagText} onKeyDown={checkForEnter} onChange={(event) => {
setTagText(event.target.value)
}}></input>
</div>
<div className={buttonRow}>
<button
className={buttonStyle(
{
size: 'slim'
}
)}
onClick={() => {
}}
>
Add Prompt
</button>
<TagTypeToggle positive={positive} setPositive={setPositive}></TagTypeToggle>
</div>
<ActiveTags></ActiveTags>
</div >
);
}

View File

@ -1,79 +0,0 @@
import { style, globalStyle } from '@vanilla-extract/css';
import { vars } from "../../../../../styles/theme/index.css";
export const PromptCreatorMain = style({
display: 'flex',
flexDirection: 'column',
width: '100%',
height: '100%',
marginBottom: 0,
});
globalStyle(`${PromptCreatorMain} input`, {
width: '100%',
});
globalStyle(`${PromptCreatorMain} > div`, {
marginBottom: vars.spacing.small,
});
export const ToggleGroupMain = style({
// '--toggle-size': '30px',
});
export const ToggleMain = style({
background: vars.backgroundDark,
height: '22px',
borderRadius: '15px',
width: '34px',
border: 0,
position: 'relative',
display: 'inline-flex',
padding: 0,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
boxShadow: `0 0 2px 0 ${vars.backgroundDark}`,
});
export const ToggleLabel = style({
});
export const ToggleEnabled = style({
});
globalStyle(`${ToggleMain}[data-headlessui-state="checked"]`, {
background: vars.backgroundLight,
});
export const TogglePill = style({
display: 'inline-flex',
height: '18px',
width: '30px',
borderRadius: '15px',
background: vars.backgroundDark,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
});
globalStyle(`${ToggleMain}[data-headlessui-state="checked"] ${TogglePill}`, {
background: vars.backgroundAccentMain,
});
globalStyle(`${TogglePill} p`, {
color: vars.colors.text.normal,
});
export const buttonRow = style({
marginTop: vars.spacing.small,
display: 'flex',
flexDirection: 'row',
});
globalStyle(`${buttonRow} > button`, {
flexGrow: 1,
marginRight: vars.spacing.medium,
});

View File

@ -1,108 +0,0 @@
import React, { useRef, ChangeEvent } from "react";
import { XButton } from "../../../../../styles/shared.css";
import {
ImageInputDisplay,
InputLabel,
ImageInput,
ImageFixer,
} from "./seedImage.css";
import {
buttonStyle
} from "../../../../_recipes/button.css";
import { useImageCreate } from "../../../../../stores/imageCreateStore";
import { useTranslation } from "react-i18next";
// TODO : figure out why this needs props to be passed in.. fixes a type error
// when the component is used in the parent component
export default function SeedImage(_props: any) {
const { t } = useTranslation();
const imageInputRef = useRef<HTMLInputElement>(null);
const initImage = useImageCreate((state) =>
state.getValueForRequestKey("init_image")
);
const isInPaintingMode = useImageCreate((state) => state.isInpainting);
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const _startFileSelect = () => {
imageInputRef.current?.click();
};
const _handleFileSelect = (event: ChangeEvent<HTMLInputElement>) => {
// @ts-expect-error
const file = event.target.files[0];
if (void 0 !== file) {
const reader = new FileReader();
reader.onload = (e) => {
if (e.target != null) {
setRequestOption("init_image", e.target.result);
}
};
reader.readAsDataURL(file);
}
};
const toggleInpainting = useImageCreate((state) => state.toggleInpainting);
const _handleClearImage = () => {
setRequestOption("init_image", undefined);
setRequestOption("mask", undefined);
if (isInPaintingMode) {
toggleInpainting();
}
};
return (
<div className={ImageInputDisplay}>
<div>
<label className={InputLabel}>
<b>{t("home.initial-img-txt")}</b>
</label>
<input
ref={imageInputRef}
className={ImageInput}
name="init_image"
type="file"
onChange={_handleFileSelect}
/>
<button className={buttonStyle()} onClick={_startFileSelect}>
{t("home.initial-img-btn")}
</button>
</div>
<div className={ImageFixer}>
{void 0 !== initImage && (
<>
<div>
<img src={initImage} width="100" height="100" />
<button className={XButton} onClick={_handleClearImage}>
X
</button>
</div>
<label>
<input
type="checkbox"
onChange={(e) => {
toggleInpainting();
}}
checked={isInPaintingMode}
></input>
{t("in-paint.txt")}
</label>
</>
)}
</div>
</div>
);
}

View File

@ -1,23 +0,0 @@
import { style } from "@vanilla-extract/css";
import { vars } from "../../../../../styles/theme/index.css";
export const ImageInputDisplay = style({
display: "flex",
});
export const InputLabel = style({
marginBottom: vars.spacing.small,
display: "block",
});
export const ImageInput = style({
display: "none",
});
// this is needed to fix an issue with the image input text
// when that is a drag an drop we can remove this
export const ImageFixer = style({
marginLeft: "20px",
});

View File

@ -1,21 +0,0 @@
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 Queue
</label>
);
}

View File

@ -1,21 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../styles/theme/index.css";
export const CreationPaneMain = style({
position: "relative",
width: "100%",
height: "100%",
overflowY: "auto",
overflowX: "hidden",
});
globalStyle(`${CreationPaneMain} > div`, {
marginBottom: vars.spacing.medium,
});
export const InpaintingSlider = style({
position: "absolute",
top: "10px",
left: "400px",
zIndex: 1,
backgroundColor: "rgba(0, 0, 0, 0.5)",
});

View File

@ -1,89 +0,0 @@
import create from "zustand";
import produce from "immer";
import { persist } from "zustand/middleware";
export interface ImageCreationUIOptions {
isOpenAdvancedSettings: boolean;
isOpenAdvImprovementSettings: boolean;
isOpenAdvPropertySettings: boolean;
isOpenAdvWorkflowSettings: boolean;
isOpenImageModifier: boolean;
showQueue: boolean;
toggleAdvancedSettings: () => void;
toggleAdvImprovementSettings: () => void;
toggleAdvPropertySettings: () => void;
toggleAdvWorkflowSettings: () => void;
toggleImageModifier: () => void;
toggleQueue: () => void;
}
export const useCreateUI = create<ImageCreationUIOptions>(
//@ts-expect-error
persist(
(set, get) => ({
isOpenAdvancedSettings: false,
isOpenAdvImprovementSettings: false,
isOpenAdvPropertySettings: false,
isOpenAdvWorkflowSettings: false,
isOpenImageModifier: false,
showQueue: false,
toggleAdvancedSettings: () => {
set(
produce((state: ImageCreationUIOptions) => {
state.isOpenAdvancedSettings = !state.isOpenAdvancedSettings;
})
);
},
toggleAdvImprovementSettings: () => {
set(
produce((state: ImageCreationUIOptions) => {
state.isOpenAdvImprovementSettings =
!state.isOpenAdvImprovementSettings;
})
);
},
toggleAdvPropertySettings: () => {
set(
produce((state: ImageCreationUIOptions) => {
state.isOpenAdvPropertySettings = !state.isOpenAdvPropertySettings;
})
);
},
toggleAdvWorkflowSettings: () => {
set(
produce((state: ImageCreationUIOptions) => {
state.isOpenAdvWorkflowSettings = !state.isOpenAdvWorkflowSettings;
})
);
},
toggleImageModifier: () => {
set(
produce((state: ImageCreationUIOptions) => {
state.isOpenImageModifier = !state.isOpenImageModifier;
})
);
},
toggleQueue: () => {
set(
produce((state: ImageCreationUIOptions) => {
state.showQueue = !state.showQueue;
})
);
},
}),
{
name: "createUI",
// getStorage: () => localStorage,
}
)
);

View File

@ -1,33 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../../styles/theme/index.css";
export const ImagerModifierGroups = style({
// marginBottom: vars.spacing.small,
paddingLeft: 0,
listStyleType: "none",
});
globalStyle(`${ImagerModifierGroups} li`, {
marginTop: vars.spacing.medium,
});
export const ImageModifierGrouping = style({
marginTop: vars.spacing.medium,
});
globalStyle(`${ImageModifierGrouping} h4`, {
fontSize: vars.fonts.sizes.Plain,
});
export const ModifierListStyle = style({
paddingLeft: 0,
listStyleType: "none",
display: "flex",
flexWrap: "wrap",
gap: vars.spacing.small,
});
globalStyle(`${ModifierListStyle} li`, {
margin: 0,
});

View File

@ -1,115 +0,0 @@
import React, { useState } from "react";
import {
MenuButton,
} from "../../../../styles/shared.css";
import {
card
} from '../../../_recipes/card.css';
import {
buttonStyle,
} from "../../../_recipes/button.css";
import {
ImagerModifierGroups,
ImageModifierGrouping,
ModifierListStyle,
} from "./imageModifiers.css";
import { ModifierObject, useImageCreate } from "../../../../stores/imageCreateStore";
import { useCreateUI } from "../creationPanelUIStore";
import ModifierTag from "../../../molecules/modifierTag";
interface ModifierListProps {
category: string;
tags: ModifierObject[];
}
function ModifierList({ tags, category }: ModifierListProps) {
return (
<ul className={ModifierListStyle}>
{tags.map((tag) => (
<li key={tag.modifier}>
<ModifierTag category={category} name={tag.modifier} previews={tag.previews} />
</li>
))}
</ul>
);
}
interface ModifierGroupingProps {
title: string;
category: string;
tags: ModifierObject[];
}
function ModifierGrouping({ title, category, tags }: ModifierGroupingProps) {
// doing this localy for now, but could move to a store
// and persist if we wanted to
const [isExpanded, setIsExpanded] = useState(false);
const _toggleExpand = () => {
setIsExpanded(!isExpanded);
};
return (
<div className={ImageModifierGrouping}>
<button type="button" className={buttonStyle({
type: 'action',
color: 'accent',
})} onClick={_toggleExpand}>
<h4>{title}</h4>
</button>
{isExpanded && <ModifierList category={category} tags={tags} />}
</div>
);
}
export default function ImageModifers() {
const allModifiers = useImageCreate((state) => state.allModifiers);
const imageModifierIsOpen = useCreateUI((state) => state.isOpenImageModifier);
const toggleImageModifiersIsOpen = useCreateUI(
(state) => state.toggleImageModifier
);
const handleClick = () => {
toggleImageModifiersIsOpen();
};
return (
<div className={card(
{
level: 1,
backing: 'normal'
}
)}>
<button
type="button"
onClick={handleClick}
className={buttonStyle({
type: 'action',
color: 'secondary',
size: 'large'
})}
>
Image Modifiers
</button>
{imageModifierIsOpen && (
<ul className={ImagerModifierGroups}>
{allModifiers.map((item, index) => {
return (
<li key={item.category}>
<ModifierGrouping title={item.category} category={item.category} tags={item.modifiers} />
</li>
);
})}
</ul>
)}
</div>
);
}

View File

@ -1,18 +0,0 @@
const Mockifiers = [
[
"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"]],
];
export default Mockifiers;

View File

@ -1,43 +0,0 @@
import React, { ChangeEvent } from "react";
import BasicCreation from "./basicCreation";
import AdvancedSettings from "./advancedSettings";
import ImageModifiers from "./imageModifiers";
import InpaintingPanel from "./inpaintingPanel";
import QueueDisplay from "../queueDisplay";
// 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 { useRequestQueue } from "../../../stores/requestQueueStore";
import {
CreationPaneMain,
InpaintingSlider,
} from "./creationPanel.css";
export default function CreationPanel() {
const isInPaintingMode = useImageCreate((state) => state.isInpainting);
const hasQueue = useRequestQueue((state) => state.hasAnyQueue());
return (
<>
<div className={CreationPaneMain}>
<BasicCreation></BasicCreation>
<AdvancedSettings></AdvancedSettings>
<ImageModifiers></ImageModifiers>
</div>
{isInPaintingMode && (
<div className={InpaintingSlider}>
<InpaintingPanel></InpaintingPanel>
</div>
)}
</>
);
}

View File

@ -1,114 +0,0 @@
import React, { useRef, useState, ChangeEvent } from "react";
import DrawImage from "../../../molecules/drawImage";
import { useImageCreate } from "../../../../stores/imageCreateStore";
import {
InpaintingPanelMain,
InpaintingControls,
InpaintingControlRow,
} from "./inpaintingPanel.css";
export default function InpaintingPanel() {
// no idea if this is the right typing
// const drawingRef = useRef(null);
const [brushSize, setBrushSize] = useState("20");
const [brushShape, setBrushShape] = useState("round");
const [brushColor, setBrushColor] = useState("#fff");
const [isErasing, setIsErasing] = useState(false);
const initImage = useImageCreate((state) =>
state.getValueForRequestKey("init_image")
);
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const setMask = (data: string) => {
setRequestOption("mask", data);
}
const _handleBrushMask = () => {
setIsErasing(false);
};
const _handleBrushErase = () => {
setIsErasing(true);
};
const _handleBrushSize = (event: ChangeEvent<HTMLInputElement>) => {
setBrushSize(event.target.value);
};
return (
<div className={InpaintingPanelMain}>
<DrawImage
// ref={drawingRef}
imageData={initImage}
// @ts-expect-error
brushSize={brushSize}
brushShape={brushShape}
brushColor={brushColor}
isErasing={isErasing}
setData={setMask}
/>
<div className={InpaintingControls}>
<div className={InpaintingControlRow}>
<button onClick={_handleBrushMask}>Mask</button>
<button onClick={_handleBrushErase}>Erase</button>
{/* <button disabled onClick={_handleFillMask}>
Fill
</button>
<button disabled onClick={_handleClearAll}>
Clear
</button> */}
<label>
Brush Size
<input
type="range"
min="1"
max="100"
value={brushSize}
onChange={_handleBrushSize}
></input>
</label>
</div>
<div className={InpaintingControlRow}>
<button
onClick={() => {
setBrushShape("round");
}}
>
Cirle Brush
</button>
<button
onClick={() => {
setBrushShape("square");
}}
>
Square Brush
</button>
{/* <button
onClick={() => {
setBrushColor("#000");
}}
>
Dark Brush
</button> */}
{/* <button
onClick={() => {
setBrushColor("#fff");
}}
>
Light Brush
</button> */}
</div>
</div>
</div>
);
}

View File

@ -1,27 +0,0 @@
import { style } from "@vanilla-extract/css";
export const InpaintingPanelMain = style({
position: "relative",
width: "100%",
height: "100%",
padding: "10px 10px",
});
export const InpaintingControls = style({
display: "flex",
flexDirection: "row",
width: "100%",
flexWrap: "wrap",
});
export const InpaintingControlRow = style({
display: "flex",
flexDirection: "row",
justifyContent: "space-evenly",
alignItems: "center",
width: "100%",
":first-of-type": {
margin: "10px 0",
},
});

View File

@ -1,12 +0,0 @@
import { style } from '@vanilla-extract/css'
// handles all 3
export const currentDisplayMain = style({
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
width: '100%',
padding: '0 0 0 0',
})

View File

@ -1,40 +0,0 @@
import { style, globalStyle } from '@vanilla-extract/css'
import { vars } from '../../../../styles/theme/index.css'
export const imageDisplayMain = style({
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
});
export const imageDisplayContainer = style({
height: '100%',
width: '80%',
display: 'flex',
justifyContent: 'center',
});
export const imageDisplayCenter = style({
width: '100%',
maxWidth: '1000px',
position: 'relative',
});
export const imageDisplayContent = style({
display: 'flex',
flexDirection: 'column',
});
globalStyle(`${imageDisplayContent} > div`, {
marginBottom: vars.spacing.medium,
});
globalStyle(`${imageDisplayContent} p`, {
marginBottom: vars.spacing.small,
});
globalStyle(`${imageDisplayContent} button`, {
marginRight: vars.spacing.medium,
});

View File

@ -1,100 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import React from "react";
import { useImageCreate } from "../../../../stores/imageCreateStore";
import { CompletedImagesType } from "../../../../stores/imageDisplayStore";
import GeneratedImage from "../../../molecules/generatedImage";
import {
imageDisplayMain,
imageDisplayContainer,
imageDisplayCenter,
imageDisplayContent,
} from './imageDisplay.css';
import {
buttonStyle
} from "../../../_recipes/button.css";
export default function ImageDisplay({ info, data }: CompletedImagesType) {
const createFileName = () => {
const {
prompt,
negative_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 (typeof use_face_correction == "string") {
fileName += `_FaceCorrection-${use_face_correction}`;
}
if (typeof use_upscale == "string") {
fileName += `_Upscale-${use_upscale}`;
}
// Add the width and height
fileName += `_${width}x${height}`;
// add the file extension
fileName += ".png";
// return fileName
return fileName;
};
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const _handleSave = () => {
const link = document.createElement("a");
link.download = createFileName();
link.href = data ?? "";
link.click();
};
const _handleUseAsInput = () => {
setRequestOption("init_image", data);
};
return (
<div className={imageDisplayMain}>
<div className={imageDisplayContainer}>
<div className={imageDisplayCenter}>
<div className={imageDisplayContent}>
<div>
<p> {info?.prompt}</p>
<p> {info?.negative_prompt}</p>
<div>
<button className={buttonStyle(
)} onClick={_handleSave}>Save</button>
<button className={buttonStyle(
{
color: "secondary",
type: "outline",
}
)} onClick={_handleUseAsInput}>Use as Input</button>
</div>
</div>
<GeneratedImage imageData={data} metadata={info}></GeneratedImage>
</div>
</div>
</div>
</div>
);
};

View File

@ -1,58 +0,0 @@
import React, { useEffect, useState } from "react";
import { FetchingStates, useImageFetching } from "../../../stores/imageFetchingStore";
import { useImageDisplay } from "../../../stores/imageDisplayStore";
import { API_URL } from "../../../api";
import {
currentDisplayMain,
} from './currentDisplay.css';
import ImageDisplay from "./imageDisplay";
const IdleDisplay = () => {
return (
<h4 className="no-image">Try Making a new image!</h4>
);
};
const LoadingDisplay = ({ images }: { images: string[] }) => {
return (
<>
{images.map((image, index) => {
if (index == images.length - 1) {
return (
<img src={`${API_URL}${image}`} key={index} />
)
}
})
}
</>
);
};
export default function CurrentDisplay() {
const status = useImageFetching((state) => state.status);
const currentImage = useImageDisplay((state) => state.currentImage);
const progressImages = useImageFetching((state) => state.progressImages);
return (
<div className={currentDisplayMain}>
{(currentImage == null) && <IdleDisplay />}
{/* {(status === FetchingStates.FETCHING || status === FetchingStates.PROGRESSING) && <LoadingDisplay />}
{(currentImage != null) && <ImageDisplay info={currentImage?.info} data={currentImage?.data} />} */}
{
(progressImages.length > 0)
? <LoadingDisplay images={progressImages} />
: (currentImage != null) && <ImageDisplay info={currentImage?.info} data={currentImage?.data} />
}
</div>
);
}

View File

@ -1,22 +0,0 @@
import { style } from '@vanilla-extract/css'
import {
card as cardStyles,
} from '../../_recipes/card.css'
export const currentInfoMain = style([
cardStyles(
{
backing: 'dark',
}
),
{
// display: 'flex',
// flexDirection: 'column',
// justifyContent: 'center',
// alignItems: 'center',
// height: '100%',
width: '250px',
padding: '0 0 0 0',
},
])

View File

@ -1,7 +0,0 @@
import React from "react";
import { currentInfoMain } from "./currentInfo.css";
export default function CurrentInfo() {
return <div className={
currentInfoMain
}> current info</div>;
}

View File

@ -1,59 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../styles/theme/index.css";
export const FooterDisplayMain = style({
color: vars.colors.text.normal,
fontSize: vars.fonts.sizes.Caption,
display: "inline-block",
// marginTop: vars.spacing.medium,
// marginBottom: vars.spacing.medium,
// TODO move this to the theme
padding: vars.spacing.small,
boxShadow:
"0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15)",
});
export const CoffeeButton = style({
height: "23px",
transform: "translateY(25%)",
});
globalStyle(`${FooterDisplayMain} a`, {
color: vars.colors.link,
textDecoration: "none",
});
globalStyle(`${FooterDisplayMain} a:hover`, {
textDecoration: "underline",
});
globalStyle(`${FooterDisplayMain} a:visited`, {
color: vars.colors.link,
});
globalStyle(`${FooterDisplayMain} a:active`, {
color: vars.colors.link,
});
globalStyle(`${FooterDisplayMain} a:focus`, {
color: vars.colors.link,
});
globalStyle(`${FooterDisplayMain} p`, {
margin: vars.spacing.min,
});
// .footer-display {
// color: #ffffff;
// display: flex;
// flex-direction: column;
// align-items: center;
// justify-content: center;
// }
// #coffeeButton {
// height: 23px;
// transform: translateY(25%);
// }

View File

@ -1,71 +0,0 @@
import React from "react";
import {
FooterDisplayMain,
CoffeeButton,
} from "./footerDisplay.css";
import { API_URL } from "../../../api";
export default function FooterDisplay() {
return (
<div className={FooterDisplayMain}>
<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"
rel="noreferrer"
>
<img src={`${API_URL}/kofi.png`} className={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"
rel="noreferrer"
>
discord community
</a>{" "}
or{" "}
<a
href="https://github.com/cmdr2/stable-diffusion-ui/issues"
target="_blank"
rel="noreferrer"
>
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"
rel="noreferrer"
>
the license
</a>
.
</p>
<p>
By using this software, you consent to the terms and conditions of the
license.
</p>
</div>
</div>
);
}

View File

@ -1,28 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../styles/theme/index.css";
export const HeaderDisplayMain = style({
color: vars.colors.text.normal,
display: "flex",
justifyContent: "space-between"
});
globalStyle(`${HeaderDisplayMain} > h1`, {
fontSize: vars.fonts.sizes.Title,
fontWeight: "bold",
marginRight: vars.spacing.medium,
});
export const HeaderTitle = style({
marginLeft: vars.spacing.large,
});
export const HeaderLinks = style({
display: "flex",
alignItems: "center",
flexGrow: 1,
justifyContent: "space-between",
maxWidth: "300px",
marginRight: vars.spacing.large,
});

View File

@ -1,5 +0,0 @@
import { style } from "@vanilla-extract/css";
export const HelpContent = style({
width: '300px',
});

View File

@ -1,61 +0,0 @@
import React from "react";
import { Popover } from '@headlessui/react';
// import { useTranslation } from "react-i18next";
import {
PopoverMain,
PopoverButtonStyle,
PopoverPanelMain,
} from "../../../_recipes/popover_headless.css";
import {
card
} from '../../../_recipes/card.css';
import {
IconFont,
SettingItem
} from "../../../../styles/shared.css";
import {
HelpContent
} from "./helpOptions.css";
export default function HelpOptions() {
return (
<Popover className={PopoverMain}>
<Popover.Button className={PopoverButtonStyle}>
<i className={[IconFont, 'fa-solid', 'fa-comments'].join(" ")}></i>
Help & Community
</Popover.Button>
<Popover.Panel className={PopoverPanelMain}>
<div className={HelpContent}>
<ul>
<li className={SettingItem}>
<a href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/Troubleshooting.md" target="_blank" rel="noreferrer">
<i className={[IconFont, 'fa-solid', 'fa-circle-question'].join(" ")}></i> Usual Problems and Solutions
</a>
</li>
<li className={SettingItem}>
<a href="https://discord.com/invite/u9yhsFmEkB" target="_blank" rel="noreferrer">
<i className={[IconFont, 'fa-brands', 'fa-discord'].join(" ")}></i> Discord user Community
</a>
</li>
<li className={SettingItem}>
<a href="https://old.reddit.com/r/StableDiffusionUI/" target="_blank" rel="noreferrer">
<i className={[IconFont, 'fa-brands', 'fa-reddit'].join(" ")}></i> Reddit Community
</a>
</li>
<li className={SettingItem}>
<a href="https://github.com/cmdr2/stable-diffusion-ui " target="_blank" rel="noreferrer">
<i className={[IconFont, 'fa-brands', 'fa-github'].join(" ")}></i> Source Code on Github
</a>
</li>
</ul>
</div>
</Popover.Panel>
</Popover>
);
};

View File

@ -1,62 +0,0 @@
import React, { useEffect, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { KEY_CONFIG, getConfig } from "../../../api";
import StatusDisplay from "./statusDisplay";
import HelpOptions from "./helpOptions";
import SystemSettings from "./systemSettings";
import { useTranslation } from "react-i18next";
import {
HeaderDisplayMain,
HeaderTitle,
HeaderLinks,
} from "./headerDisplay.css";
// import LanguageDropdown from "./languageDropdown";
export default function HeaderDisplay() {
const { t } = useTranslation();
const { status, data } = useQuery([KEY_CONFIG], getConfig);
const [version, setVersion] = useState("2.1.0");
const [release, setRelease] = useState("");
// this is also in the Beta Mode
// TODO: make this a custom hook
useEffect(() => {
if (status === "success") {
// TODO also pass down the actual version
const { update_branch: updateBranch } = data;
// just hard coded for now
setVersion("v2.1");
if (updateBranch === "main") {
setRelease("(stable)");
} else {
setRelease("(beta)");
}
}
}, [status, data, setVersion, setVersion]);
return (
<div className={HeaderDisplayMain}>
<div className={HeaderTitle}>
<h1>
{t("title")} {version} {release}{" "}
</h1>
<StatusDisplay className="status-display"></StatusDisplay>
</div>
<div className={HeaderLinks}>
<HelpOptions></HelpOptions>
<SystemSettings></SystemSettings>
</div>
{/* <LanguageDropdown></LanguageDropdown> */}
</div>
);
}

View File

@ -1,23 +0,0 @@
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
const LanguageDropdown = () => {
const { i18n } = useTranslation();
const [language, setLanguage] = useState("id");
const handleLangChange = (evt: any) => {
const lang = evt.target.value;
console.log(lang);
setLanguage(lang);
i18n.changeLanguage(lang);
};
return (
<select onChange={handleLangChange} value={language}>
<option value="en">EN</option>
<option value="es">ES</option>
</select>
);
};
export default LanguageDropdown;

View File

@ -1,57 +0,0 @@
import React, { useEffect, useState, useRef } from "react";
import { useQuery } from "@tanstack/react-query";
import { healthPing, HEALTH_PING_INTERVAL } from "../../../../api";
import AudioDing from "../../../molecules/audioDing";
import {
StartingStatus,
ErrorStatus,
SuccessStatus,
} from "./statusDisplay.css";
const startingMessage = "Stable Diffusion is starting...";
const successMessage = "Stable Diffusion is ready to use!";
const errorMessage = "Stable Diffusion is not running!";
export default function StatusDisplay({ className }: { className?: string }) {
const [statusMessage, setStatusMessage] = useState(startingMessage);
const [statusClass, setStatusClass] = useState(StartingStatus);
const dingRef = useRef<HTMLAudioElement>();
// but this will be moved to the status display when it is created
const { status, data } = useQuery(["health"], healthPing, {
refetchInterval: HEALTH_PING_INTERVAL,
});
useEffect(() => {
if (status === "loading") {
setStatusMessage(startingMessage);
setStatusClass(StartingStatus);
} else if (status === "error") {
setStatusMessage(errorMessage);
setStatusClass(ErrorStatus);
} else if (status === "success") {
if (data[0] === "OK") {
setStatusMessage(successMessage);
setStatusClass(SuccessStatus);
// catch an auto play error
dingRef.current?.play().catch((e) => {
console.log('DING!')
});
} else {
setStatusMessage(errorMessage);
setStatusClass(ErrorStatus);
}
}
}, [status, data, dingRef]);
return (
<>
<AudioDing ref={dingRef}></AudioDing>
<p className={[statusClass, className].join(" ")}>{statusMessage}</p>
</>
);
}

View File

@ -1,15 +0,0 @@
import { style } from "@vanilla-extract/css";
import { vars } from "../../../../styles/theme/index.css";
export const StartingStatus = style({
color: `hsl(${vars.warningHue}, ${vars.colorMod.saturation.normal}, ${vars.colorMod.lightness.normal})`,
});
export const ErrorStatus = style({
color: `hsl(${vars.errorHue}, ${vars.colorMod.saturation.normal}, ${vars.colorMod.lightness.normal})`,
});
export const SuccessStatus = style({
color: `hsl(${vars.successHue}, ${vars.colorMod.saturation.normal}, ${vars.colorMod.lightness.normal})`,
});

View File

@ -1,162 +0,0 @@
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
import React from "react";
import { Popover } from '@headlessui/react';
import { useTranslation } from "react-i18next";
import { useImageCreate } from "../../../../stores/imageCreateStore";
import BetaMode from "../../../molecules/betaMode";
import {
IconFont,
SettingItem
} from "../../../../styles/shared.css";
import {
PopoverMain,
PopoverButtonStyle,
PopoverPanelMain,
} from "../../../_recipes/popover_headless.css";
import {
SettingContent
} from "./systemSettings.css";
// import {
// SwitchGroupMain,
// SwitchMain,
// SwitchLabel,
// SwitchEnabled,
// SwitchPill,
// } from "../../../_headless/switch/index.css";
export default function SystemSettings() {
const { t } = useTranslation();
const isUseAutoSave = useImageCreate((state) => state.isUseAutoSave());
const diskPath = useImageCreate((state) =>
state.getValueForRequestKey("save_to_disk_path")
);
const turbo = useImageCreate((state) => state.getValueForRequestKey("turbo"));
const useCpu = useImageCreate((state) =>
state.getValueForRequestKey("use_cpu")
);
const useFullPrecision = useImageCreate((state) =>
state.getValueForRequestKey("use_full_precision")
);
const isSoundEnabled = useImageCreate((state) => state.isSoundEnabled());
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const toggleUseAutoSave = useImageCreate((state) => state.toggleUseAutoSave);
const toggleSoundEnabled = useImageCreate(
(state) => state.toggleSoundEnabled
);
return (
<Popover className={PopoverMain}>
<Popover.Button className={PopoverButtonStyle}>
<i className={[IconFont, 'fa-solid', 'fa-gear'].join(" ")}></i>
Settings
</Popover.Button>
<Popover.Panel className={PopoverPanelMain}>
<div className={SettingContent}>
<h4>System Settings</h4>
<ul>
<li className={SettingItem}>
<label>
<input
checked={isUseAutoSave}
onChange={(e) => toggleUseAutoSave()}
type="checkbox"
/>
{t("storage.ast")}{" "}
</label>
<label>
<input
value={diskPath}
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>
<li className={SettingItem}>
<label>
<input
checked={isSoundEnabled}
onChange={(e) => toggleSoundEnabled()}
type="checkbox"
/>
{t("advanced-settings.sound")}
</label>
{/* <Switch.Group>
<Switch.Label passive> <>{t("advanced-settings.sound")}</> </Switch.Label>
<Switch checked={isSoundEnabled} onChange={toggleSoundEnabled} className={SwitchMain}>
<span
className={SwitchPill}
/>
</Switch>
</Switch.Group> */}
</li>
<li className={SettingItem}>
<label>
<input
checked={turbo}
onChange={(e) => setRequestOption("turbo", e.target.checked)}
type="checkbox"
/>
{t("advanced-settings.turbo")} {t("advanced-settings.turbo-disc")}
</label>
</li>
<li className={SettingItem}>
<label>
<input
type="checkbox"
checked={useCpu}
onChange={(e) => setRequestOption("use_cpu", e.target.checked)}
/>
{t("advanced-settings.cpu")} {t("advanced-settings.cpu-disc")}
</label>
</li>
<li className={SettingItem}>
<label>
<input
checked={useFullPrecision}
onChange={(e) =>
setRequestOption("use_full_precision", e.target.checked)
}
type="checkbox"
/>
{t("advanced-settings.gpu")} {t("advanced-settings.gpu-disc")}
</label>
</li>
<li className={SettingItem}>
<BetaMode />
</li>
</ul>
</div>
</Popover.Panel>
</Popover>
);
}

View File

@ -1,5 +0,0 @@
import { style } from "@vanilla-extract/css";
export const SettingContent = style({
width: '480px',
});

View File

@ -1,52 +0,0 @@
import React from "react";
import { ImageRequest } from "../../../api";
import { QueuedRequest, useRequestQueue } from "../../../stores/requestQueueStore";
import {
QueueDisplayMain,
QueueListButtons,
} from "./queueDisplay.css";
import {
buttonStyle
} from "../../_recipes/button.css";
import ClearQueue from "../../molecules/clearQueue";
import QueueItem from "./queueItem";
export default function QueueDisplay() {
const requests: QueuedRequest[] = useRequestQueue((state) => state.requests);
const removeCompleted = useRequestQueue((state) => state.removeCompleted);
const removeErrored = useRequestQueue((state) => state.removeErrored);
const clearCompleted = () => {
removeCompleted();
}
const clearErrored = () => {
removeErrored();
}
return (
<div className={QueueDisplayMain}>
<ClearQueue />
<div className={QueueListButtons}>
<button
className={buttonStyle({
type: 'outline',
})}
onClick={clearCompleted}>Clear Completed</button>
<button
className={buttonStyle({
type: 'outline',
})}
onClick={clearErrored}>Clear Errored</button>
</div>
{requests.map((request) => {
return <QueueItem key={request.id} request={request}></QueueItem>;
})}
</div>
);
};

View File

@ -1,28 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../styles/theme/index.css";
export const QueueDisplayMain = style({
display: "flex",
flexDirection: "column",
width: '100%',
height: '100%',
});
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,
});

View File

@ -1,125 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import React from "react";
import { QueueStatus, QueuedRequest, useRequestQueue } from '../../../../stores/requestQueueStore';
import StopButton from '../../../molecules/stopButton';
import {
QueueItemMain,
QueueItemInfo,
QueueButtons,
} from "./queueItem.css";
import {
buttonStyle
} from "../../../_recipes/button.css";
interface QueueItemProps {
request: QueuedRequest;
}
export default function QueueItem({ request }: QueueItemProps) {
const removeItem = useRequestQueue((state) => state.removeItem);
const updateStatus = useRequestQueue((state) => state.updateStatus);
const sendPendingToTop = useRequestQueue((state) => state.sendPendingToTop);
const {
id,
options: {
prompt,
num_outputs,
seed,
sampler,
guidance_scale,
num_inference_steps,
},
status,
} = request;
const removeFromQueue = () => {
removeItem(id);
}
const pauseItem = () => {
updateStatus(id, QueueStatus.paused);
}
const retryRequest = () => {
updateStatus(id, QueueStatus.pending);
}
const sendToTop = () => {
sendPendingToTop(id);
}
return (
<div className={[QueueItemMain, status].join(' ')}>
<div className={QueueItemInfo}>
<p>{prompt}</p>
<p>Making {num_outputs} concurrent images</p>
<p>
<span>Seed: {seed} </span>
<span>Sampler: {sampler} </span>
<span>Guidance Scale: {guidance_scale} </span>
<span>Num Inference Steps: {num_inference_steps} </span>
</p>
</div>
<div className={QueueButtons}>
{status === QueueStatus.processing && (
<StopButton></StopButton>
)}
{status === QueueStatus.complete && (
<button
className={buttonStyle({
size: "large",
})}
onClick={removeFromQueue}>
Clear
</button>
)}
{status === QueueStatus.pending && (
<>
<button className={buttonStyle({
color: "cancel",
})} onClick={removeFromQueue}>Remove</button>
<button className={buttonStyle({
color: "secondary",
type: "outline",
})} onClick={pauseItem}>Pause</button>
<button className={buttonStyle({
color: "tertiary",
type: "action",
})} onClick={sendToTop}>Send to top</button>
</>
)}
{status === QueueStatus.paused && (
<button
className={buttonStyle({
size: "large",
})} onClick={retryRequest}>Resume</button>
)}
{status === QueueStatus.error && (
<button
className={buttonStyle({
size: "large",
})} onClick={retryRequest}>Retry</button>
)}
</div>
</div>
);
}

View File

@ -1,61 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../../../styles/theme/index.css";
import { QueueStatus } from "../../../../stores/requestQueueStore";
import {
card
} from '../../../_recipes/card.css';
export const QueueItemMain = style([card(
{
info: true,
level: 1
}
), {
display: "flex",
flexDirection: "column",
width: "100%",
marginBottom: vars.spacing.medium,
}]);
export const QueueItemInfo = style({
});
globalStyle(`${QueueItemInfo} p`, {
marginBottom: vars.spacing.small,
});
globalStyle(`${QueueItemMain}.${QueueStatus.complete}`, {
borderColor: `hsl(${vars.secondaryHue}, ${vars.colorMod.saturation.normal}, ${vars.colorMod.lightness.normal})`,
});
globalStyle(`${QueueItemMain}.${QueueStatus.processing}`, {
borderColor: `hsl(${vars.tertiaryHue}, ${vars.colorMod.saturation.bright}, ${vars.colorMod.lightness.bright})`,
});
globalStyle(`${QueueItemMain}.${QueueStatus.pending}`, {
borderColor: `hsl(${vars.backgroundAccentMain}, ${vars.colorMod.saturation.bright}, ${vars.colorMod.lightness.normal})`,
});
globalStyle(`${QueueItemMain}.${QueueStatus.paused}`, {
borderColor: `hsl(${vars.backgroundAccentMain}, ${vars.colorMod.saturation.dim}, ${vars.colorMod.lightness.dim})`,
backgroundColor: `hsl(${vars.backgroundAccentMain}, ${vars.colorMod.saturation.dim}, ${vars.colorMod.lightness.dim})`,
});
globalStyle(`${QueueItemMain}.${QueueStatus.error}`, {
borderColor: `hsl(${vars.errorHue}, ${vars.colorMod.saturation.normal}, ${vars.colorMod.lightness.normal})`,
});
export const QueueButtons = style({
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
});

View File

@ -1,35 +0,0 @@
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 "./styles/index.css.ts";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
staleTime: Infinity,
cacheTime: 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>
);

View File

@ -1,144 +0,0 @@
[
[
"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"
]
]
]

View File

@ -1,9 +0,0 @@
import React from "react";
export default function Beta() {
return (
<div>
<h1>Beta</h1>
</div>
);
}

View File

@ -1,54 +0,0 @@
import { style, globalStyle } from "@vanilla-extract/css";
import { vars } from "../../styles/theme/index.css";
export const AppLayout = style({
position: "relative",
width: "100vw",
height: "100vh",
pointerEvents: "auto",
display: "grid",
backgroundColor: vars.backgroundMain,
gridTemplateColumns: "400px 1fr",
gridTemplateRows: "70px 1fr 115px",
gridTemplateAreas: `
"header header header"
"create display display"
"create display display"
`,
// "@media": {
// "screen and (max-width: 800px)": {
// gridTemplateColumns: "1fr",
// gridTemplateRows: "100px 300px 1fr",
// gridTemplateAreas: `
// "header"
// "create"
// "display"
// `,
// },
// },
});
export const HeaderLayout = style({
gridArea: "header",
});
export const CreateLayout = style({
gridArea: "create",
position: "relative",
display: "flex",
flexDirection: "column",
});
export const DisplayLayout = style({
gridArea: "display",
overflow: "auto",
});
export const FooterLayout = style({
gridArea: "footer",
display: "flex",
justifyContent: "center",
});

View File

@ -1,73 +0,0 @@
import React, { useEffect } from "react";
import {
AppLayout,
HeaderLayout,
CreateLayout,
DisplayLayout,
FooterLayout,
} from "./home.css";
import { useQuery } from "@tanstack/react-query";
import { getSaveDirectory, loadModifications } from "../../api";
import Mockifiers from "../../components/organisms/creationPanel/imageModifiers/modifiers.mock";
import { useImageCreate } from "../../stores/imageCreateStore";
// Todo - import components here
import HeaderDisplay from "../../components/organisms/headerDisplay";
import BasicDisplay from "../../components/layouts/basicDisplay";
import FooterDisplay from "../../components/organisms/footerDisplay";
import CreationTabs from "../../components/layouts/creationTabs";
function Home() {
// Get the original save directory
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
const { status: statusSave, data: dataSave } = useQuery(
["SaveDir"],
getSaveDirectory
);
const { status: statusMods, data: dataMoads } = useQuery(
["modifications"],
loadModifications
);
const setAllModifiers = useImageCreate((state) => state.setAllModifiers);
useEffect(() => {
if (statusSave === "success") {
setRequestOption("save_to_disk_path", dataSave);
}
}, [setRequestOption, statusSave, dataSave]);
useEffect(() => {
if (statusMods === "success") {
setAllModifiers(dataMoads);
} else if (statusMods === "error") {
// @ts-expect-error
setAllModifiers(Mockifiers);
}
}, [setRequestOption, statusMods, dataMoads]);
return (
<>
<div className={[AppLayout].join(" ")}>
<header className={HeaderLayout}>
<HeaderDisplay></HeaderDisplay>
</header>
<nav className={CreateLayout}>
<CreationTabs></CreationTabs>
</nav>
<main className={DisplayLayout}>
<BasicDisplay></BasicDisplay>
</main>
</div>
<footer className={FooterLayout}>
<FooterDisplay></FooterDisplay>
</footer>
</>
);
}
export default Home;

View File

@ -1,9 +0,0 @@
import React from "react";
export default function Settings() {
return (
<div>
<h1>Settings</h1>
</div>
);
}

View File

@ -1,397 +0,0 @@
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
import create from "zustand";
import produce from "immer";
import { devtools } from "zustand/middleware";
import { useRandomSeed } from "../utils";
import { ImageRequest } from "../api";
export interface ImageCreationUiOptions {
isUseRandomSeed: boolean;
isUseAutoSave: boolean;
isSoundEnabled: boolean;
}
export const SAMPLER_OPTIONS = [
'plms',
'ddim',
'heun',
'euler',
'euler_a',
'dpm2',
'dpm2_a',
'lms',
] as const;
export interface ModifierPreview {
name: string;
path: string;
}
export interface ModifierObject {
category?: string;
modifier: string;
previews: ModifierPreview[];
}
interface ModifiersList {
category: string;
modifiers: ModifierObject[];
}
type ModifiersOptionList = ModifiersList[];
export interface promptTag {
id: string;
name: string;
type: 'positive' | 'negative';
}
interface ImageCreateState {
parallelCount: number;
requestOptions: ImageRequest;
allModifiers: ModifiersOptionList;
createTags: promptTag[];
// negativeTags: promptTag[];
tagMap: Record<string, string[]>;
isInpainting: boolean;
setParallelCount: (count: number) => void;
setRequestOptions: (key: keyof ImageRequest, value: any) => void;
getValueForRequestKey: (key: keyof ImageRequest) => any;
setAllModifiers: (modifiers: ModifiersOptionList) => void;
setModifierOptions: (key: string, value: any) => void;
toggleTag: (category: string, tag: string) => void;
hasTag: (category: string, tag: string) => boolean;
selectedTags: () => ModifierObject[];
addCreateTag: (tag: promptTag) => void;
removeCreateTag: (id: string) => void;
changeCreateTagType: (id: string, type: 'positive' | 'negative') => void;
reorderCreateTag: (tag: promptTag, index: number) => void;
builtRequest: () => ImageRequest;
uiOptions: ImageCreationUiOptions;
toggleUseUpscaling: () => void;
// isUsingUpscaling: () => boolean
toggleUseFaceCorrection: () => void;
isUsingFaceCorrection: () => boolean;
isUsingUpscaling: () => boolean;
toggleUseRandomSeed: () => void;
isRandomSeed: () => boolean;
toggleUseAutoSave: () => void;
isUseAutoSave: () => boolean;
toggleSoundEnabled: () => void;
isSoundEnabled: () => boolean;
toggleInpainting: () => void;
}
// devtools breaks TS
export const useImageCreate = create<ImageCreateState>(
// @ts-expect-error
devtools((set, get) => ({
parallelCount: 1,
requestOptions: {
session_id: new Date().getTime().toString(),
prompt: "a photograph of an astronaut riding a horse",
negative_prompt: "",
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: "GFPGANv1.3",
use_upscale: "RealESRGAN_x4plus",
show_only_filtered_image: true,
init_image: undefined,
sampler: "plms",
stream_progress_updates: true,
stream_image_progress: false,
mask: undefined,
},
// selected tags
createTags: [] as promptTag[],
// negativeTags: [] as promptTag[],
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
tagMap: {} as Record<string, string[]>,
uiOptions: {
// TODO proper persistence of all UI / user settings centrally somewhere?
// localStorage.getItem('ui:advancedSettingsIsOpen') === 'true',
isUseRandomSeed: true,
isUseAutoSave: false,
isSoundEnabled: false,
},
allModifiers: [] as ModifiersOptionList,
isInpainting: false,
setParallelCount: (count: number) =>
set(
produce((state) => {
state.parallelCount = count;
})
),
setRequestOptions: (key: keyof ImageRequest, value: any) => {
set(
produce((state) => {
state.requestOptions[key] = value;
})
);
},
getValueForRequestKey: (key: keyof ImageRequest) => {
return get().requestOptions[key];
},
setAllModifiers: (modifiers: ModifiersOptionList) => {
set(
produce((state) => {
state.allModifiers = modifiers;
})
);
},
toggleTag: (category: string, tag: string) => {
set(
produce((state) => {
if (Object.keys(state.tagMap).includes(category)) {
if (state.tagMap[category].includes(tag)) {
state.tagMap[category] = state.tagMap[category].filter((t: string) => t !== tag);
} else {
state.tagMap[category].push(tag);
}
} else {
state.tagMap[category] = [tag];
}
})
);
},
hasTag: (category: string, tag: string) => {
return get().tagMap[category]?.includes(tag);
},
selectedTags: () => {
// get all the modifiers and all the tags
const allModifiers = get().allModifiers;
const selectedTags = get().tagMap;
let selected: ModifierObject[] = [];
// for each mappped tag
for (const [category, tags] of Object.entries(selectedTags)) {
// find the modifier
const modifier = allModifiers.find((m) => m.category === category);
if (modifier) {
// for each tag in the modifier
for (const tag of tags) {
// find the tag
const tagObject = modifier.modifiers.find((m) => m.modifier === tag);
if (tagObject) {
// add the previews to the selected list
selected = selected.concat({
...tagObject,
category: modifier.category
});
}
}
}
}
return selected;
},
addCreateTag: (tag: promptTag) => {
set(
produce((state) => {
state.createTags.push(tag);
})
);
},
removeCreateTag: (id: string) => {
set(
produce((state) => {
// @ts-expect-error
state.createTags = state.createTags.filter((t) => t.id !== id);
})
);
},
changeCreateTagType: (id: string, type: 'positive' | 'negative') => {
set(
produce((state) => {
// @ts-expect-error
const tag = state.createTags.find((t) => t.id === id);
if (tag) {
tag.type = type;
}
})
);
},
reorderCreateTag: (tag: promptTag, index: number) => {
set(
produce((state) => {
const tagIndex = state.createTags.indexOf(tag);
if (tagIndex !== -1) {
state.createTags.splice(tagIndex, 1);
state.createTags.splice(index, 0, tag);
}
})
);
},
// the request body to send to the server
// this is a computed value, just adding the tags to the request
builtRequest: () => {
const state = get();
const requestOptions = state.requestOptions;
const selectedTags = get().selectedTags();
const tags = selectedTags.map((t: ModifierObject) => t.modifier);
const positivePrompt = state.createTags.filter((t) => t.type === "positive").map((t) => t.name).join(",");
const negativePrompt = state.createTags.filter((t) => t.type === "negative").map((t) => t.name).join(",");
// join all the tags with a comma and add it to the prompt
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
// const prompt = `${requestOptions.prompt}, ${tags.join(",")}`;
const request = {
...requestOptions,
prompt: positivePrompt,
negative_prompt: negativePrompt,
};
// 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;
}
if (void 0 === request.init_image) {
request.prompt_strength = undefined;
}
// a bit of a hack. figure out what a clean value to pass to the server is
// if we arent using upscaling clear the upscaling
if (request.use_upscale === "") {
request.use_upscale = null;
}
// make sure you look above for the "null" value
// this patches over a a backend issue if you dont ask for a filtered image
// you get nothing back
if (
null === request.use_upscale &&
null === request.use_face_correction
) {
request.show_only_filtered_image = false;
}
return request;
},
toggleUseFaceCorrection: () => {
set(
produce((state) => {
const isSeting =
typeof state.getValueForRequestKey("use_face_correction") ===
"string"
? null
: "GFPGANv1.3";
state.requestOptions.use_face_correction = isSeting;
})
);
},
isUsingFaceCorrection: () => {
const isUsing =
typeof get().getValueForRequestKey("use_face_correction") === "string";
return isUsing;
},
isUsingUpscaling: () => {
const isUsing = get().getValueForRequestKey("use_upscale") !== "";
return isUsing;
},
toggleUseRandomSeed: () => {
set(
produce((state: ImageCreateState) => {
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: ImageCreateState) => {
state.uiOptions.isUseAutoSave = !state.uiOptions.isUseAutoSave;
// localStorage.setItem(
// "ui:isUseAutoSave",
// state.uiOptions.isUseAutoSave
// );
})
);
},
isUseAutoSave: () => {
return get().uiOptions.isUseAutoSave;
},
toggleSoundEnabled: () => {
set(
produce((state: ImageCreateState) => {
state.uiOptions.isSoundEnabled = !state.uiOptions.isSoundEnabled;
})
);
},
isSoundEnabled: () => {
return get().uiOptions.isSoundEnabled;
},
toggleInpainting: () => {
set(
produce((state: ImageCreateState) => {
state.isInpainting = !state.isInpainting;
})
);
},
}))
);

View File

@ -1,54 +0,0 @@
import create from "zustand";
import produce from "immer";
import { ImageRequest } from "../api";
export interface CompletedImagesType {
id?: string;
data: string | undefined;
info: ImageRequest;
}
interface ImageDisplayState {
// imageOptions: Map<string, any>;
images: CompletedImagesType[]
currentImage: CompletedImagesType | null
updateDisplay: (id: string, ImageData: string, imageOptions: any) => void;
setCurrentImage: (image: CompletedImagesType) => void;
clearDisplay: () => void;
}
export const useImageDisplay = create<ImageDisplayState>((set, get) => ({
imageMap: new Map<string, any>(),
images: [],
currentImage: null,
// use produce to make sure we don't mutate state
// imageOptions: any
updateDisplay: (id: string, ImageData: string, imageOptions) => {
set(
produce((state) => {
state.currentImage = { id, display: ImageData, info: imageOptions };
state.images.unshift({ id, data: ImageData, info: imageOptions });
state.currentImage = state.images[0];
})
);
},
setCurrentImage: (image) => {
set(
produce((state) => {
state.currentImage = image;
})
);
},
clearDisplay: () => {
set(
produce((state) => {
state.images = [];
state.currentImage = null;
})
);
}
}));

View File

@ -1,113 +0,0 @@
import create from "zustand";
import produce from "immer";
export const FetchingStates = {
IDLE: "IDLE",
FETCHING: "FETCHING",
PROGRESSING: "PROGRESSING",
SUCCEEDED: "SUCCEEDED",
COMPLETE: "COMPLETE",
ERROR: "ERROR",
} as const;
interface ImageFetchingState {
status: typeof FetchingStates[keyof typeof FetchingStates];
step: number;
totalSteps: number;
data: string;
progressImages: string[]
timeStarted: Date;
timeNow: Date;
appendData: (data: string) => void;
reset: () => void;
setStatus: (status: typeof FetchingStates[keyof typeof FetchingStates]) => void;
setStep: (step: number) => void;
setTotalSteps: (totalSteps: number) => void;
addProgressImage: (imageLink: string) => void;
setStartTime: () => void;
setNowTime: () => void;
resetForFetching: () => void;
}
export const useImageFetching = create<ImageFetchingState>((set) => ({
status: FetchingStates.IDLE,
step: 0,
totalSteps: 0,
data: '',
progressImages: [],
timeStarted: new Date(),
timeNow: new Date(),
// use produce to make sure we don't mutate state
appendData: (data: string) => {
set(
produce((state: ImageFetchingState) => {
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
state.data += data;
})
);
},
reset: () => {
set(
produce((state: ImageFetchingState) => {
state.status = FetchingStates.IDLE;
state.step = 0;
state.totalSteps = 0;
state.data = '';
})
);
},
setStatus: (status: typeof FetchingStates[keyof typeof FetchingStates]) => {
set(
produce((state: ImageFetchingState) => {
state.status = status;
})
);
},
setStep: (step: number) => {
set(
produce((state: ImageFetchingState) => {
state.step = step;
})
);
},
setTotalSteps: (totalSteps: number) => {
set(
produce((state: ImageFetchingState) => {
state.totalSteps = totalSteps;
})
);
},
addProgressImage: (imageLink: string) => {
set(
produce((state: ImageFetchingState) => {
state.progressImages.push(imageLink);
})
);
},
setStartTime: () => {
set(
produce((state: ImageFetchingState) => {
state.timeStarted = new Date();
})
);
},
setNowTime: () => {
set(
produce((state: ImageFetchingState) => {
state.timeNow = new Date();
})
);
},
resetForFetching: () => {
set(
produce((state: ImageFetchingState) => {
state.status = FetchingStates.FETCHING;
state.progressImages = [];
state.step = 0;
state.totalSteps = 0;
state.timeNow = new Date();
state.timeStarted = new Date();
})
);
}
}));

Some files were not shown because too many files have changed in this diff Show More