mirror of
https://github.com/easydiffusion/easydiffusion.git
synced 2025-01-04 05:19:12 +01:00
commit
2590dc690e
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -2,4 +2,10 @@ __pycache__
|
||||
installer
|
||||
installer.tar
|
||||
dist
|
||||
|
||||
# built code for the front end
|
||||
!/ui/frontend/dist
|
||||
ui/frontend/.idea/*
|
||||
ui/frontend/build_src/.idea/*
|
||||
|
||||
.idea/*
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
251
ui/frontend/assets/modifiers.json
Normal file
251
ui/frontend/assets/modifiers.json
Normal file
@ -0,0 +1,251 @@
|
||||
[
|
||||
[
|
||||
"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",
|
||||
"Art Nouveau",
|
||||
"Bauhaus",
|
||||
"Baroque",
|
||||
"CGI",
|
||||
"Cartoon",
|
||||
"Comic Book",
|
||||
"Concept Art",
|
||||
"Constructivist",
|
||||
"Cubist",
|
||||
"Digital Art",
|
||||
"Dadaist",
|
||||
"Expressionist",
|
||||
"Fantasy",
|
||||
"Fauvist",
|
||||
"Figurative",
|
||||
"Graphic Novel",
|
||||
"Geometric",
|
||||
"Hard Edge Painting",
|
||||
"Hydrodipped",
|
||||
"Impressionistic",
|
||||
"Lithography",
|
||||
"Manga",
|
||||
"Minimalist",
|
||||
"Modern Art",
|
||||
"Mosaic",
|
||||
"Mural",
|
||||
"Naive",
|
||||
"Neoclassical",
|
||||
"Photo",
|
||||
"Realistic",
|
||||
"Rococo",
|
||||
"Romantic",
|
||||
"Street Art",
|
||||
"Symbolist",
|
||||
"Stuckist",
|
||||
"Surrealist",
|
||||
"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",
|
||||
"Landscape",
|
||||
"Lens Flare",
|
||||
"Macro",
|
||||
"Polaroid",
|
||||
"Photoshoot",
|
||||
"Portrait",
|
||||
"Studio Lighting",
|
||||
"Vintage",
|
||||
"War Photography",
|
||||
"White Balance",
|
||||
"Wildlife Photography"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Color",
|
||||
[
|
||||
"Beautiful Lighting",
|
||||
"Cold Color Palette",
|
||||
"Colorful",
|
||||
"Dynamic Lighting",
|
||||
"Electric Colors",
|
||||
"Infrared",
|
||||
"Pastel",
|
||||
"Neon",
|
||||
"Synthwave",
|
||||
"Warm Color Palette"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Emotions",
|
||||
[
|
||||
"Angry",
|
||||
"Bitter",
|
||||
"Disgusted",
|
||||
"Embarrassed",
|
||||
"Evil",
|
||||
"Excited",
|
||||
"Fear",
|
||||
"Funny",
|
||||
"Happy",
|
||||
"Horrifying",
|
||||
"Lonely",
|
||||
"Sad",
|
||||
"Serene",
|
||||
"Surprised",
|
||||
"Melancholic"
|
||||
]
|
||||
],
|
||||
[
|
||||
"Style of an artist or community",
|
||||
[
|
||||
"Artstation",
|
||||
"trending on Artstation",
|
||||
"by Agnes Lawrence Pelton",
|
||||
"by Akihito Yoshida",
|
||||
"by Alex Grey",
|
||||
"by Alexander Jansson",
|
||||
"by Alphonse Mucha",
|
||||
"by Andy Warhol",
|
||||
"by Artgerm",
|
||||
"by Asaf Hanuka",
|
||||
"by Aubrey Beardsley",
|
||||
"by Banksy",
|
||||
"by Beeple",
|
||||
"by Ben Enwonwu",
|
||||
"by Bob Eggleton",
|
||||
"by Caravaggio Michelangelo Merisi",
|
||||
"by Caspar David Friedrich",
|
||||
"by Chris Foss",
|
||||
"by Claude Monet",
|
||||
"by Dan Mumford",
|
||||
"by David Mann",
|
||||
"by Diego Velázquez",
|
||||
"by Disney Animation Studios",
|
||||
"by Édouard Manet",
|
||||
"by Esao Andrews",
|
||||
"by Frida Kahlo",
|
||||
"by Gediminas Pranckevicius",
|
||||
"by Georgia O'Keeffe",
|
||||
"by Greg Rutkowski",
|
||||
"by Gustave Doré",
|
||||
"by Gustave Klimt",
|
||||
"by H.R. Giger",
|
||||
"by Hayao Miyazaki",
|
||||
"by Henri Matisse",
|
||||
"by HP Lovecraft",
|
||||
"by Ivan Shishkin",
|
||||
"by Jack Kirby",
|
||||
"by Jackson Pollock",
|
||||
"by James Jean",
|
||||
"by Jim Burns",
|
||||
"by Johannes Vermeer",
|
||||
"by John William Waterhouse",
|
||||
"by Katsushika Hokusai",
|
||||
"by Kim Tschang Yeul",
|
||||
"by Ko Young Hoon",
|
||||
"by Leonardo da Vinci",
|
||||
"by Lisa Frank",
|
||||
"by M.C. Escher",
|
||||
"by Mahmoud Saïd",
|
||||
"by Makoto Shinkai",
|
||||
"by Marc Simonetti",
|
||||
"by Mark Brooks",
|
||||
"by Michelangelo",
|
||||
"by Pablo Picasso",
|
||||
"by Paul Klee",
|
||||
"by Peter Mohrbacher",
|
||||
"by Pierre-Auguste Renoir",
|
||||
"by Pixar Animation Studios",
|
||||
"by Rembrandt",
|
||||
"by Richard Dadd",
|
||||
"by Rossdraws",
|
||||
"by Salvador Dalí",
|
||||
"by Sam Does Arts",
|
||||
"by Sandro Botticelli",
|
||||
"by Ted Nasmith",
|
||||
"by Ten Hundred",
|
||||
"by Thomas Kinkade",
|
||||
"by Tivadar Csontváry Kosztka",
|
||||
"by Victo Ngai",
|
||||
"by Vincent Di Fate",
|
||||
"by Vincent van Gogh",
|
||||
"by Wes Anderson",
|
||||
"by wlop",
|
||||
"by Yoshitaka Amano"
|
||||
]
|
||||
],
|
||||
[
|
||||
"CGI Software",
|
||||
[
|
||||
"3D Model",
|
||||
"3D Sculpt",
|
||||
"3Ds Max Model",
|
||||
"Blender Model",
|
||||
"Cinema4d Model",
|
||||
"Maya Model",
|
||||
"Unreal Engine",
|
||||
"Zbrush Sculpt"
|
||||
]
|
||||
],
|
||||
[
|
||||
"CGI Rendering",
|
||||
[
|
||||
"3D Render",
|
||||
"Corona Render",
|
||||
"Creature Design",
|
||||
"Cycles Render",
|
||||
"Detailed Render",
|
||||
"Environment Design",
|
||||
"Intricate Environment",
|
||||
"LSD Render",
|
||||
"Octane Render",
|
||||
"PBR",
|
||||
"Glass Caustics",
|
||||
"Global Illumination",
|
||||
"Subsurface Scattering"
|
||||
]
|
||||
]
|
||||
]
|
87
ui/frontend/build_src/.eslintrc.cjs
Normal file
87
ui/frontend/build_src/.eslintrc.cjs
Normal file
@ -0,0 +1,87 @@
|
||||
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
|
||||
};
|
18
ui/frontend/build_src/.gitignore
vendored
Normal file
18
ui/frontend/build_src/.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# local ignores - We could move these to the global ignores,
|
||||
# but I think it makes sense to keep them here
|
||||
|
||||
# env
|
||||
*.local
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# installed dependencies
|
||||
node_modules
|
||||
|
14
ui/frontend/build_src/index.html
Normal file
14
ui/frontend/build_src/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="color-scheme" content="dark light" />
|
||||
<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>
|
8195
ui/frontend/build_src/package-lock.json
generated
Normal file
8195
ui/frontend/build_src/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
54
ui/frontend/build_src/package.json
Normal file
54
ui/frontend/build_src/package.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"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": {
|
||||
"@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/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"
|
||||
}
|
||||
}
|
||||
}
|
7
ui/frontend/build_src/prettier.cjs
Normal file
7
ui/frontend/build_src/prettier.cjs
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
semi: true,
|
||||
trailingComma: "es5",
|
||||
endOfLine: "lf",
|
||||
};
|
28
ui/frontend/build_src/src/App.tsx
Normal file
28
ui/frontend/build_src/src/App.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import React, { useState } from "react";
|
||||
import { ReactLocation, Router } from "@tanstack/react-location";
|
||||
import Home from "./pages/Home";
|
||||
import Settings from "./pages/Settings";
|
||||
// @ts-expect-error
|
||||
import { darkTheme, lightTheme } from "./styles/theme/index.css.ts";
|
||||
import "./Translation/config";
|
||||
const location = new ReactLocation();
|
||||
|
||||
function App() {
|
||||
// just check for the theme one 1 time
|
||||
|
||||
// var { matches } = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const matches = true;
|
||||
const themeClass = matches ? darkTheme : lightTheme;
|
||||
|
||||
return (
|
||||
<Router
|
||||
location={location}
|
||||
routes={[
|
||||
{ path: "/", element: <Home className={themeClass} /> },
|
||||
{ path: "/settings", element: <Settings className={themeClass} /> },
|
||||
]}
|
||||
></Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
32
ui/frontend/build_src/src/Translation/config.ts
Normal file
32
ui/frontend/build_src/src/Translation/config.ts
Normal file
@ -0,0 +1,32 @@
|
||||
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");
|
||||
});
|
108
ui/frontend/build_src/src/Translation/locales/en/home.json
Normal file
108
ui/frontend/build_src/src/Translation/locales/en/home.json
Normal file
@ -0,0 +1,108 @@
|
||||
{
|
||||
"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"
|
||||
}
|
108
ui/frontend/build_src/src/Translation/locales/es/home.json
Normal file
108
ui/frontend/build_src/src/Translation/locales/es/home.json
Normal file
@ -0,0 +1,108 @@
|
||||
{
|
||||
"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"
|
||||
}
|
85
ui/frontend/build_src/src/api/index.ts
Normal file
85
ui/frontend/build_src/src/api/index.ts
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* basic server health
|
||||
*/
|
||||
|
||||
import type { ImageRequest } 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 ImageOutput {
|
||||
data: string;
|
||||
path_abs: string | null;
|
||||
seed: number;
|
||||
}
|
||||
|
||||
export interface ImageReturnType {
|
||||
output: ImageOutput[];
|
||||
request: {};
|
||||
status: string;
|
||||
session_id: 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),
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
return data;
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
import { useImageCreate } from "../../../stores/imageCreateStore";
|
||||
|
||||
interface ModifierTagProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default function ModifierTag({ name }: ModifierTagProps) {
|
||||
const hasTag = useImageCreate((state) => state.hasTag(name))
|
||||
? "selected"
|
||||
: "";
|
||||
const toggleTag = useImageCreate((state) => state.toggleTag);
|
||||
|
||||
const _toggleTag = () => {
|
||||
toggleTag(name);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={"modifierTag " + hasTag} onClick={_toggleTag}>
|
||||
<p>{name}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
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
|
||||
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>
|
||||
);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
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",
|
||||
});
|
@ -0,0 +1,180 @@
|
||||
// @ts-nocheck
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
|
||||
import {
|
||||
DrawImageMain, // @ts-expect-error
|
||||
} from "./drawImage.css.ts";
|
||||
|
||||
// https://github.com/embiem/react-canvas-draw
|
||||
|
||||
interface DrawImageProps {
|
||||
imageData: string;
|
||||
brushSize: string;
|
||||
|
||||
brushShape: string;
|
||||
brushColor: string;
|
||||
isErasing: boolean;
|
||||
}
|
||||
|
||||
export default function DrawImage({
|
||||
imageData,
|
||||
brushSize,
|
||||
brushShape,
|
||||
brushColor,
|
||||
isErasing,
|
||||
}: 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");
|
||||
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();
|
||||
// TODO: SEND THIS TO THE STATE
|
||||
}
|
||||
};
|
||||
|
||||
const _drawCanvas = (x, y, brushSize, brushShape, brushColor) => {
|
||||
const canvas = drawingRef.current;
|
||||
if (canvas != null) {
|
||||
const ctx = canvas.getContext("2d");
|
||||
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;
|
||||
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");
|
||||
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;
|
||||
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");
|
||||
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>
|
||||
);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
export const generatedImageMain = style({
|
||||
position: "relative",
|
||||
});
|
||||
|
||||
// export const imageContain = style({
|
||||
// width: "512px",
|
||||
// height: "512px",
|
||||
// backgroundColor: "black",
|
||||
// display: "flex",
|
||||
// justifyContent: "center",
|
||||
// alignItems: "center",
|
||||
// });
|
||||
|
||||
export const image = style({
|
||||
// width: "512px",
|
||||
// height: "512px",
|
||||
// objectFit: "contain",
|
||||
});
|
@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
|
||||
import { ImageRequest } from "../../../stores/imageCreateStore";
|
||||
|
||||
import {
|
||||
generatedImageMain,
|
||||
image, // @ts-expect-error
|
||||
} from "./generatedImage.css.ts";
|
||||
|
||||
interface GeneretaedImageProps {
|
||||
imageData: string | undefined;
|
||||
metadata: ImageRequest | undefined;
|
||||
className?: string;
|
||||
// children: never[];
|
||||
}
|
||||
|
||||
export default function GeneratedImage({
|
||||
imageData,
|
||||
metadata,
|
||||
className,
|
||||
}: GeneretaedImageProps) {
|
||||
return (
|
||||
<div className={[generatedImageMain, className].join(" ")}>
|
||||
<img className={image} src={imageData} alt={metadata!.prompt} />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import { style, globalStyle } from "@vanilla-extract/css";
|
||||
|
||||
// @ts-expect-error
|
||||
import { vars } from "../../../../styles/theme/index.css.ts";
|
||||
|
||||
// import { PanelBox } from "../../../../styles/shared.css.ts";
|
||||
|
||||
export const AdvancedSettingsList = style({
|
||||
// marginBottom: vars.spacing.small,
|
||||
paddingLeft: 0,
|
||||
listStyleType: "none",
|
||||
});
|
||||
|
||||
export const AdvancedSettingGrouping = style({
|
||||
marginTop: vars.spacing.medium,
|
||||
});
|
||||
|
||||
export const MenuButton = style({
|
||||
display: "block",
|
||||
width: "100%",
|
||||
textAlign: "left",
|
||||
backgroundColor: "transparent",
|
||||
color: vars.colors.text.normal,
|
||||
border: "0 none",
|
||||
cursor: "pointer",
|
||||
padding: "0",
|
||||
marginBottom: vars.spacing.medium,
|
||||
});
|
||||
|
||||
globalStyle(`${MenuButton}> h4`, {
|
||||
color: "#e7ba71",
|
||||
});
|
@ -0,0 +1,74 @@
|
||||
import React from "react";
|
||||
import { useImageCreate } from "../../../../../stores/imageCreateStore";
|
||||
|
||||
import { useCreateUI } from "../../creationPanelUIStore";
|
||||
|
||||
import {
|
||||
SettingItem, // @ts-expect-error
|
||||
} from "../../../../../styles/shared.css.ts";
|
||||
|
||||
import {
|
||||
MenuButton, // @ts-expect-error
|
||||
} from "../advancedsettings.css.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function GpuSettings() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
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 setRequestOption = useImageCreate((state) => state.setRequestOptions);
|
||||
|
||||
const gpuOpen = useCreateUI((state) => state.isOpenAdvGPUSettings);
|
||||
const toggleGpuOpen = useCreateUI((state) => state.toggleAdvGPUSettings);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button type="button" className={MenuButton} onClick={toggleGpuOpen}>
|
||||
<h4>GPU Settings</h4>
|
||||
</button>
|
||||
{gpuOpen && (
|
||||
<>
|
||||
<div 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>
|
||||
</div>
|
||||
<div 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>
|
||||
</div>
|
||||
<div 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>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useImageCreate } from "../../../../../stores/imageCreateStore";
|
||||
|
||||
import { useCreateUI } from "../../creationPanelUIStore";
|
||||
|
||||
import {
|
||||
SettingItem, // @ts-expect-error
|
||||
} from "../../../../../styles/shared.css.ts";
|
||||
import {
|
||||
MenuButton, // @ts-expect-error
|
||||
} from "../advancedsettings.css.ts";
|
||||
|
||||
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={MenuButton}
|
||||
onClick={toggleImprovementOpen}
|
||||
>
|
||||
<h4>Improvement Settings</h4>
|
||||
</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>
|
||||
);
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useCreateUI } from "../creationPanelUIStore";
|
||||
|
||||
// @ts-expect-error
|
||||
import { PanelBox } from "../../../../styles/shared.css.ts";
|
||||
|
||||
import {
|
||||
AdvancedSettingsList,
|
||||
AdvancedSettingGrouping, // @ts-expect-error
|
||||
} from "./advancedsettings.css.ts";
|
||||
|
||||
import ImprovementSettings from "./improvementSettings";
|
||||
import PropertySettings from "./propertySettings";
|
||||
import WorkflowSettings from "./workflowSettings";
|
||||
import GpuSettings from "./gpuSettings";
|
||||
|
||||
// import BetaMode from "../../../molecules/betaMode";
|
||||
|
||||
function SettingsList() {
|
||||
return (
|
||||
<ul className={AdvancedSettingsList}>
|
||||
<li className={AdvancedSettingGrouping}>
|
||||
<ImprovementSettings />
|
||||
</li>
|
||||
<li className={AdvancedSettingGrouping}>
|
||||
<PropertySettings />
|
||||
</li>
|
||||
<li className={AdvancedSettingGrouping}>
|
||||
<WorkflowSettings />
|
||||
</li>
|
||||
<li className={AdvancedSettingGrouping}>
|
||||
<GpuSettings />
|
||||
</li>
|
||||
|
||||
{/* <li className={AdvancedSettingGrouping}>
|
||||
<BetaMode />
|
||||
</li> */}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AdvancedSettings() {
|
||||
const advancedSettingsIsOpen = useCreateUI(
|
||||
(state) => state.isOpenAdvancedSettings
|
||||
);
|
||||
|
||||
const toggleAdvancedSettingsIsOpen = useCreateUI(
|
||||
(state) => state.toggleAdvancedSettings
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={PanelBox}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleAdvancedSettingsIsOpen}
|
||||
className="panel-box-toggle-btn"
|
||||
>
|
||||
<h3>Advanced Settings</h3>
|
||||
</button>
|
||||
{advancedSettingsIsOpen && <SettingsList />}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
import React, { useState } from "react";
|
||||
import { useImageCreate } from "../../../../../stores/imageCreateStore";
|
||||
import { useCreateUI } from "../../creationPanelUIStore";
|
||||
|
||||
import {
|
||||
SettingItem, // @ts-expect-error
|
||||
} from "../../../../../styles/shared.css.ts";
|
||||
|
||||
import {
|
||||
MenuButton, // @ts-expect-error
|
||||
} from "../advancedsettings.css.ts";
|
||||
|
||||
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 propertyOpen = useCreateUI((state) => state.isOpenAdvPropertySettings);
|
||||
const togglePropertyOpen = useCreateUI(
|
||||
(state) => state.toggleAdvPropertySettings
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button type="button" className={MenuButton} onClick={togglePropertyOpen}>
|
||||
<h4>Property Settings</h4>
|
||||
</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>
|
||||
);
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
import React from "react";
|
||||
import { useImageCreate } from "../../../../../stores/imageCreateStore";
|
||||
|
||||
import { useCreateUI } from "../../creationPanelUIStore";
|
||||
|
||||
import {
|
||||
SettingItem, // @ts-expect-error
|
||||
} from "../../../../../styles/shared.css.ts";
|
||||
|
||||
import {
|
||||
MenuButton, // @ts-expect-error
|
||||
} from "../advancedsettings.css.ts";
|
||||
|
||||
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 isUseAutoSave = useImageCreate((state) => state.isUseAutoSave());
|
||||
const diskPath = useImageCreate((state) =>
|
||||
state.getValueForRequestKey("save_to_disk_path")
|
||||
);
|
||||
const isSoundEnabled = useImageCreate((state) => state.isSoundEnabled());
|
||||
|
||||
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
|
||||
const setParallelCount = useImageCreate((state) => state.setParallelCount);
|
||||
const toggleUseAutoSave = useImageCreate((state) => state.toggleUseAutoSave);
|
||||
const toggleSoundEnabled = useImageCreate(
|
||||
(state) => state.toggleSoundEnabled
|
||||
);
|
||||
|
||||
const workflowOpen = useCreateUI((state) => state.isOpenAdvWorkflowSettings);
|
||||
const toggleWorkflowOpen = useCreateUI(
|
||||
(state) => state.toggleAdvWorkflowSettings
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button type="button" className={MenuButton} onClick={toggleWorkflowOpen}>
|
||||
<h4>Workflow Settings</h4>
|
||||
</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>
|
||||
<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>
|
||||
</div>
|
||||
<div className={SettingItem}>
|
||||
<label>
|
||||
<input
|
||||
checked={isSoundEnabled}
|
||||
onChange={(e) => toggleSoundEnabled()}
|
||||
type="checkbox"
|
||||
/>
|
||||
{t("advanced-settings.sound")}
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
|
||||
import { useImageCreate } from "../../../../../stores/imageCreateStore";
|
||||
import ModifierTag from "../../../../atoms/modifierTag";
|
||||
|
||||
export default function ActiveTags() {
|
||||
const selectedtags = useImageCreate((state) => state.selectedTags());
|
||||
return (
|
||||
<div className="selected-tags">
|
||||
<p>Active Tags</p>
|
||||
<ul>
|
||||
{selectedtags.map((tag) => (
|
||||
<li key={tag}>
|
||||
<ModifierTag name={tag}></ModifierTag>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { style, globalStyle } from "@vanilla-extract/css";
|
||||
|
||||
export const CreationBasicMain = style({
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
});
|
||||
|
||||
globalStyle(`${CreationBasicMain} > *`, {
|
||||
marginBottom: "10px",
|
||||
});
|
||||
|
||||
export const PromptDisplay = style({});
|
||||
|
||||
globalStyle(`${PromptDisplay} > p`, {
|
||||
fontSize: "1.5em",
|
||||
fontWeight: "bold",
|
||||
marginBottom: "10px",
|
||||
});
|
||||
|
||||
globalStyle(`${PromptDisplay} > textarea`, {
|
||||
width: "100%",
|
||||
resize: "vertical",
|
||||
height: "100px",
|
||||
});
|
@ -0,0 +1,40 @@
|
||||
import React, { ChangeEvent } from "react";
|
||||
import { useImageCreate } from "../../../../stores/imageCreateStore";
|
||||
|
||||
import {
|
||||
CreationBasicMain,
|
||||
PromptDisplay, // @ts-expect-error
|
||||
} from "./basicCreation.css.ts";
|
||||
|
||||
import SeedImage from "./seedImage";
|
||||
import ActiveTags from "./activeTags";
|
||||
import MakeButton from "./makeButton";
|
||||
|
||||
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}>
|
||||
<div className={PromptDisplay}>
|
||||
<p>{t("home.editor-title")}</p>
|
||||
<textarea value={promptText} onChange={handlePromptChange}></textarea>
|
||||
</div>
|
||||
<MakeButton></MakeButton>
|
||||
|
||||
<SeedImage></SeedImage>
|
||||
|
||||
<ActiveTags></ActiveTags>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import React from "react";
|
||||
|
||||
import { useImageCreate } from "../../../../../stores/imageCreateStore";
|
||||
import { useImageQueue } from "../../../../../stores/imageQueueStore";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { useRandomSeed } from "../../../../../utils";
|
||||
|
||||
import {
|
||||
MakeButtonStyle, // @ts-expect-error
|
||||
} from "./makeButton.css.ts";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function MakeButton() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const parallelCount = useImageCreate((state) => state.parallelCount);
|
||||
const builtRequest = useImageCreate((state) => state.builtRequest);
|
||||
const addNewImage = useImageQueue((state) => state.addNewImage);
|
||||
const hasQueue = useImageQueue((state) => state.hasQueuedImages());
|
||||
const isRandomSeed = useImageCreate((state) => state.isRandomSeed());
|
||||
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
|
||||
|
||||
const makeImages = () => {
|
||||
// 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();
|
||||
// the actual number of request we will make
|
||||
const requests = [];
|
||||
// the number of images we will make
|
||||
let { num_outputs } = req;
|
||||
|
||||
// if making fewer images than the parallel count
|
||||
// then it is only 1 request
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make the requests
|
||||
requests.forEach((num, index) => {
|
||||
// get the seed we want to use
|
||||
let seed = req.seed;
|
||||
if (index !== 0) {
|
||||
debugger;
|
||||
// we want to use a random seed for subsequent requests
|
||||
seed = useRandomSeed();
|
||||
}
|
||||
// add the request to the queue
|
||||
addNewImage(uuidv4(), {
|
||||
...req,
|
||||
// updated the number of images to make
|
||||
num_outputs: num,
|
||||
// update the seed
|
||||
seed,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
className={MakeButtonStyle}
|
||||
onClick={makeImages}
|
||||
disabled={hasQueue}
|
||||
>
|
||||
{t("home.make-img-btn")}
|
||||
</button>
|
||||
);
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
// @ts-expect-error
|
||||
import { vars } from "../../../../../styles/theme/index.css.ts";
|
||||
|
||||
export const MakeButtonStyle = style({
|
||||
width: "100%",
|
||||
backgroundColor: vars.colors.brand,
|
||||
fontSize: vars.fonts.sizes.Headline,
|
||||
fontWeight: "bold",
|
||||
color: vars.colors.text.normal,
|
||||
padding: vars.spacing.small,
|
||||
borderRadius: vars.trim.smallBorderRadius,
|
||||
|
||||
":hover": {
|
||||
backgroundColor: vars.colors.brandHover,
|
||||
},
|
||||
|
||||
":active": {
|
||||
backgroundColor: vars.colors.brandActive,
|
||||
},
|
||||
|
||||
":disabled": {
|
||||
backgroundColor: vars.colors.brandDimmed,
|
||||
color: vars.colors.text.dimmed,
|
||||
},
|
||||
|
||||
":focus": {
|
||||
outline: "none",
|
||||
},
|
||||
});
|
@ -0,0 +1,100 @@
|
||||
import React, { useRef, ChangeEvent } from "react";
|
||||
|
||||
import {
|
||||
ImageInputDisplay,
|
||||
InputLabel,
|
||||
ImageInput,
|
||||
ImageInputButton,
|
||||
ImageFixer,
|
||||
XButton, // @ts-expect-error
|
||||
} from "./seedImage.css.ts";
|
||||
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);
|
||||
|
||||
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={ImageInputButton} 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>
|
||||
);
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
// @ts-expect-error
|
||||
import { vars } from "../../../../../styles/theme/index.css.ts";
|
||||
|
||||
export const ImageInputDisplay = style({
|
||||
display: "flex",
|
||||
});
|
||||
|
||||
export const InputLabel = style({
|
||||
marginBottom: vars.spacing.small,
|
||||
display: "block",
|
||||
});
|
||||
|
||||
export const ImageInput = style({
|
||||
display: "none",
|
||||
});
|
||||
|
||||
export const ImageInputButton = style({
|
||||
backgroundColor: vars.colors.brand,
|
||||
fontSize: vars.fonts.sizes.Subheadline,
|
||||
fontWeight: "bold",
|
||||
color: vars.colors.text.normal,
|
||||
padding: vars.spacing.small,
|
||||
borderRadius: vars.trim.smallBorderRadius,
|
||||
|
||||
":hover": {
|
||||
backgroundColor: vars.colors.brandHover,
|
||||
},
|
||||
|
||||
":active": {
|
||||
backgroundColor: vars.colors.brandActive,
|
||||
},
|
||||
|
||||
":disabled": {
|
||||
backgroundColor: vars.colors.brandDimmed,
|
||||
color: vars.colors.text.dimmed,
|
||||
},
|
||||
});
|
||||
|
||||
// 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",
|
||||
});
|
||||
|
||||
// just a 1 off component for now
|
||||
// dont bother bringing in line with the rest of the app
|
||||
export const XButton = style({
|
||||
position: "absolute",
|
||||
transform: "translateX(-50%) translateY(-35%)",
|
||||
background: "black",
|
||||
color: "white",
|
||||
border: "2pt solid #ccc",
|
||||
padding: "0",
|
||||
cursor: "pointer",
|
||||
outline: "inherit",
|
||||
borderRadius: "8pt",
|
||||
width: "16pt",
|
||||
height: "16pt",
|
||||
fontFamily: "Verdana",
|
||||
fontSize: "8pt",
|
||||
});
|
@ -0,0 +1,56 @@
|
||||
.create-layout {
|
||||
padding: 10px;
|
||||
}
|
||||
/* .panel-box-toggle-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
border: 0 none;
|
||||
cursor: pointer;
|
||||
} */
|
||||
|
||||
.selected-tags {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.selected-tags ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.modifier-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.modifierTag {
|
||||
display: inline-block;
|
||||
padding: 6px;
|
||||
background-color: rgb(38, 77, 141);
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.modifierTag.selected {
|
||||
background-color: rgb(131, 11, 121);
|
||||
}
|
||||
|
||||
.modifierTag p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
/* Dont show the file name */
|
||||
color: transparent;
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
import create from "zustand";
|
||||
import produce from "immer";
|
||||
import { persist } from "zustand/middleware";
|
||||
|
||||
export interface ImageCreationUIOptions {
|
||||
isOpenAdvancedSettings: boolean;
|
||||
isOpenAdvImprovementSettings: boolean;
|
||||
isOpenAdvPropertySettings: boolean;
|
||||
isOpenAdvWorkflowSettings: boolean;
|
||||
isOpenAdvGPUSettings: boolean;
|
||||
|
||||
isOpenImageModifier: boolean;
|
||||
imageMofidiersMap: object;
|
||||
|
||||
toggleAdvancedSettings: () => void;
|
||||
toggleAdvImprovementSettings: () => void;
|
||||
toggleAdvPropertySettings: () => void;
|
||||
toggleAdvWorkflowSettings: () => void;
|
||||
toggleAdvGPUSettings: () => void;
|
||||
|
||||
toggleImageModifier: () => void;
|
||||
// addImageModifier: (modifier: string) => void;
|
||||
}
|
||||
|
||||
export const useCreateUI = create<ImageCreationUIOptions>(
|
||||
//@ts-expect-error
|
||||
persist(
|
||||
(set, get) => ({
|
||||
isOpenAdvancedSettings: false,
|
||||
isOpenAdvImprovementSettings: false,
|
||||
isOpenAdvPropertySettings: false,
|
||||
isOpenAdvWorkflowSettings: false,
|
||||
isOpenAdvGPUSettings: false,
|
||||
isOpenImageModifier: false,
|
||||
imageMofidiersMap: {},
|
||||
|
||||
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;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
toggleAdvGPUSettings: () => {
|
||||
set(
|
||||
produce((state: ImageCreationUIOptions) => {
|
||||
state.isOpenAdvGPUSettings = !state.isOpenAdvGPUSettings;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
toggleImageModifier: () => {
|
||||
set(
|
||||
produce((state: ImageCreationUIOptions) => {
|
||||
state.isOpenImageModifier = !state.isOpenImageModifier;
|
||||
})
|
||||
);
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: "createUI",
|
||||
// getStorage: () => localStorage,
|
||||
}
|
||||
)
|
||||
);
|
@ -0,0 +1,17 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
export const CreationPaneMain = style({
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
padding: "0 10px",
|
||||
overflowY: "auto",
|
||||
});
|
||||
|
||||
export const InpaintingSlider = style({
|
||||
position: "absolute",
|
||||
top: "10px",
|
||||
left: "400px",
|
||||
zIndex: 1,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
import { style, globalStyle } from "@vanilla-extract/css";
|
||||
// @ts-expect-error
|
||||
import { vars } from "../../../../styles/theme/index.css.ts";
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
export const MenuButton = style({
|
||||
display: "block",
|
||||
width: "100%",
|
||||
textAlign: "left",
|
||||
backgroundColor: "transparent",
|
||||
color: vars.colors.text.normal,
|
||||
border: "0 none",
|
||||
cursor: "pointer",
|
||||
padding: "0",
|
||||
marginBottom: vars.spacing.medium,
|
||||
});
|
||||
|
||||
globalStyle(`${MenuButton}> h4`, {
|
||||
color: "#e7ba71",
|
||||
});
|
||||
|
||||
export const ModifierListStyle = style({
|
||||
// marginBottom: vars.spacing.small,
|
||||
paddingLeft: 0,
|
||||
listStyleType: "none",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
});
|
||||
|
||||
globalStyle(`${ModifierListStyle} li`, {
|
||||
margin: 0,
|
||||
});
|
@ -0,0 +1,96 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
// @ts-expect-error
|
||||
import { PanelBox } from "../../../../styles/shared.css.ts";
|
||||
|
||||
import {
|
||||
ImagerModifierGroups,
|
||||
ImageModifierGrouping,
|
||||
MenuButton,
|
||||
ModifierListStyle, //@ts-expect-error
|
||||
} from "./imageModifiers.css.ts";
|
||||
|
||||
import { useImageCreate } from "../../../../stores/imageCreateStore";
|
||||
import { useCreateUI } from "../creationPanelUIStore";
|
||||
|
||||
import ModifierTag from "../../../atoms/modifierTag";
|
||||
|
||||
interface ModifierListProps {
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
function ModifierList({ tags }: ModifierListProps) {
|
||||
return (
|
||||
<ul className={ModifierListStyle}>
|
||||
{tags.map((tag) => (
|
||||
<li key={tag}>
|
||||
<ModifierTag name={tag} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
interface ModifierGroupingProps {
|
||||
title: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
function ModifierGrouping({ title, tags }: ModifierGroupingProps) {
|
||||
// doing this localy for now, but could move to a store
|
||||
// and persist if we wanted to
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const _toggleExpand = () => {
|
||||
setIsExpanded(!isExpanded);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={ImageModifierGrouping}>
|
||||
<button type="button" className={MenuButton} onClick={_toggleExpand}>
|
||||
<h4>{title}</h4>
|
||||
</button>
|
||||
{isExpanded && <ModifierList 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={PanelBox}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClick}
|
||||
className="panel-box-toggle-btn"
|
||||
>
|
||||
{/* TODO: swap this manual collapse stuff out for some UI component? */}
|
||||
<h3>Image Modifiers (art styles, tags, ect)</h3>
|
||||
</button>
|
||||
|
||||
{imageModifierIsOpen && (
|
||||
<ul className={ImagerModifierGroups}>
|
||||
{allModifiers.map((item, index) => {
|
||||
return (
|
||||
// @ts-expect-error
|
||||
<li key={item[0]}>
|
||||
{/* @ts-expect-error */}
|
||||
<ModifierGrouping title={item[0]} tags={item[1]} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
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;
|
@ -0,0 +1,160 @@
|
||||
import {
|
||||
createGlobalTheme,
|
||||
createThemeContract,
|
||||
createTheme,
|
||||
} from "@vanilla-extract/css";
|
||||
|
||||
/**
|
||||
* Colors are all the same across the themes, this is just to set up a contract
|
||||
* Colors can be decided later. I am just the architect.
|
||||
* Tried to pull things from the original app.
|
||||
*
|
||||
* Lots of these arent used yet, but once they are defined and useable then they can be set.
|
||||
*/
|
||||
|
||||
// Link color 0, 102, 204
|
||||
|
||||
const colors = createThemeContract({
|
||||
brand: null,
|
||||
brandDimmed: null,
|
||||
brandHover: null,
|
||||
brandActive: null,
|
||||
brandAccent: null,
|
||||
brandAccentDimmed: null,
|
||||
brandAccentActive: null,
|
||||
|
||||
secondary: null,
|
||||
secondaryDimmed: null,
|
||||
secondaryHover: null,
|
||||
secondaryActive: null,
|
||||
secondaryAccent: null,
|
||||
secondaryAccentDimmed: null,
|
||||
secondaryAccentActive: null,
|
||||
|
||||
background: null,
|
||||
backgroundAccent: null,
|
||||
backgroundAlt: null,
|
||||
backgroundAltAccent: null,
|
||||
|
||||
text: {
|
||||
normal: null,
|
||||
dimmed: null,
|
||||
|
||||
secondary: null,
|
||||
secondaryDimmed: null,
|
||||
|
||||
accent: null,
|
||||
accentDimmed: null,
|
||||
},
|
||||
|
||||
link: null,
|
||||
warning: null,
|
||||
error: null,
|
||||
success: null,
|
||||
});
|
||||
|
||||
const app = createGlobalTheme(":root", {
|
||||
spacing: {
|
||||
small: "5px",
|
||||
medium: "10px",
|
||||
large: "25px",
|
||||
},
|
||||
|
||||
trim: {
|
||||
smallBorderRadius: "5px",
|
||||
},
|
||||
|
||||
fonts: {
|
||||
body: "Arial, Helvetica, sans-serif;",
|
||||
sizes: {
|
||||
Title: "2em",
|
||||
Headline: "1.5em",
|
||||
Subheadline: "1.20em",
|
||||
SubSubheadline: "1em",
|
||||
Body: "1em",
|
||||
Caption: ".75em",
|
||||
Overline: ".5em",
|
||||
},
|
||||
},
|
||||
colors: colors,
|
||||
});
|
||||
|
||||
export const darkTheme = createTheme(colors, {
|
||||
brand: "#5000b9", // purple
|
||||
brandDimmed: "#433852", // muted purple
|
||||
brandHover: "#5d00d6", // bringhter purple
|
||||
brandActive: "#5d00d6", // bringhter purple
|
||||
brandAccent: "#28004e", // darker purple
|
||||
brandAccentDimmed: "#28004e", // darker purple
|
||||
brandAccentActive: "#28004e", // darker purple
|
||||
|
||||
secondary: "#0b8334", // green
|
||||
secondaryDimmed: "#0b8334", // green
|
||||
secondaryHover: "#0b8334", // green
|
||||
secondaryActive: "#0b8334", // green
|
||||
secondaryAccent: "#0b8334", // green
|
||||
secondaryAccentDimmed: "#0b8334", // green
|
||||
secondaryAccentActive: "#0b8334", // green
|
||||
|
||||
background: "#202124", // dark grey
|
||||
backgroundAccent: " #383838", // lighter grey
|
||||
backgroundAlt: "#2c2d30", // med grey
|
||||
backgroundAltAccent: "#383838", // lighter grey
|
||||
|
||||
text: {
|
||||
normal: "#ffffff", // white
|
||||
dimmed: "#d1d5db", // off white
|
||||
|
||||
secondary: "#ffffff", // white
|
||||
secondaryDimmed: "#d1d5db", // off white
|
||||
|
||||
accent: "#e7ba71", // orange
|
||||
accentDimmed: "#7d6641", // muted orange
|
||||
},
|
||||
|
||||
link: "#0066cc", // blue
|
||||
warning: "#f0ad4e",
|
||||
error: "#d9534f",
|
||||
success: "#5cb85c",
|
||||
});
|
||||
|
||||
// Generated by co-pilot
|
||||
export const lightTheme = createTheme(colors, {
|
||||
brand: "#1E40AF",
|
||||
brandDimmed: "#1E40AF",
|
||||
brandHover: "#1E40AF",
|
||||
brandActive: "#1E40AF",
|
||||
brandAccent: "#1E40AF",
|
||||
brandAccentDimmed: "#1E40AF",
|
||||
brandAccentActive: "#1E40AF",
|
||||
|
||||
secondary: "#DB2777",
|
||||
secondaryDimmed: "#DB2777",
|
||||
secondaryHover: "#DB2777",
|
||||
secondaryActive: "#DB2777",
|
||||
secondaryAccent: "#DB2777",
|
||||
secondaryAccentDimmed: "#DB2777",
|
||||
secondaryAccentActive: "#DB2777",
|
||||
|
||||
background: "#EFF6FF",
|
||||
backgroundAccent: "#EFF6FF",
|
||||
backgroundAlt: "#EFF6FF",
|
||||
backgroundAltAccent: "#EFF6FF",
|
||||
text: {
|
||||
normal: "#1F2937",
|
||||
dimmed: "#6B7280",
|
||||
|
||||
secondary: "#1F2937",
|
||||
secondaryDimmed: "#6B7280",
|
||||
|
||||
accent: "#1F2937",
|
||||
accentDimmed: "#6B7280",
|
||||
},
|
||||
|
||||
link: "#0066cc", // blue
|
||||
warning: "yellow",
|
||||
error: "red",
|
||||
success: "green",
|
||||
});
|
||||
|
||||
export const vars = { ...app, colors };
|
@ -0,0 +1,38 @@
|
||||
import React, { ChangeEvent } from "react";
|
||||
|
||||
import AdvancedSettings from "./advancedSettings";
|
||||
import ImageModifiers from "./imageModifiers";
|
||||
import InpaintingPanel from "./inpaintingPanel";
|
||||
|
||||
// this works but causes type errors so its not worth it for now
|
||||
// import { useImageCreate } from "@stores/imageCreateStore.ts";
|
||||
|
||||
import { useImageCreate } from "../../../stores/imageCreateStore";
|
||||
|
||||
import "./creationPanel.css";
|
||||
|
||||
import {
|
||||
CreationPaneMain,
|
||||
InpaintingSlider, // @ts-expect-error
|
||||
} from "./creationpane.css.ts";
|
||||
|
||||
import BasicCreation from "./basicCreation";
|
||||
|
||||
export default function CreationPanel() {
|
||||
const isInPaintingMode = useImageCreate((state) => state.isInpainting);
|
||||
return (
|
||||
<>
|
||||
<div className={CreationPaneMain}>
|
||||
<BasicCreation></BasicCreation>
|
||||
<AdvancedSettings></AdvancedSettings>
|
||||
<ImageModifiers></ImageModifiers>
|
||||
</div>
|
||||
|
||||
{isInPaintingMode && (
|
||||
<div className={InpaintingSlider}>
|
||||
<InpaintingPanel></InpaintingPanel>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
import React, { useRef, useState, ChangeEvent } from "react";
|
||||
import DrawImage from "../../../molecules/drawImage";
|
||||
|
||||
import { useImageCreate } from "../../../../stores/imageCreateStore";
|
||||
|
||||
import {
|
||||
InpaintingPanelMain,
|
||||
InpaintingControls,
|
||||
InpaintingControlRow, // @ts-expect-error
|
||||
} from "./inpaintingPanel.css.ts";
|
||||
|
||||
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 _handleBrushMask = () => {
|
||||
setIsErasing(false);
|
||||
};
|
||||
|
||||
const _handleBrushErase = () => {
|
||||
setIsErasing(true);
|
||||
};
|
||||
|
||||
const _handleFillMask = () => {
|
||||
console.log("fill mask!!", drawingRef);
|
||||
|
||||
// drawingRef.current?.fillCanvas();
|
||||
};
|
||||
|
||||
const _handleClearAll = () => {
|
||||
console.log("clear all");
|
||||
};
|
||||
|
||||
const _handleBrushSize = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setBrushSize(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={InpaintingPanelMain}>
|
||||
<DrawImage
|
||||
// ref={drawingRef}
|
||||
imageData={initImage}
|
||||
brushSize={brushSize}
|
||||
brushShape={brushShape}
|
||||
brushColor={brushColor}
|
||||
isErasing={isErasing}
|
||||
/>
|
||||
<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>
|
||||
);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
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",
|
||||
},
|
||||
});
|
@ -0,0 +1,15 @@
|
||||
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;
|
@ -0,0 +1,60 @@
|
||||
import { style, globalStyle } from "@vanilla-extract/css";
|
||||
|
||||
// @ts-expect-error
|
||||
import { vars } from "../../../../styles/theme/index.css.ts";
|
||||
|
||||
export const completedImagesMain = style({
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
paddingBottom: vars.spacing.medium,
|
||||
});
|
||||
|
||||
export const completedImagesList = style({
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "nowrap",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
overflow: "auto",
|
||||
paddingLeft: vars.spacing.none,
|
||||
});
|
||||
|
||||
globalStyle(`${completedImagesMain} li`, {
|
||||
position: "relative",
|
||||
});
|
||||
|
||||
globalStyle(`${completedImagesMain} > li:first-of-type`, {
|
||||
marginLeft: vars.spacing.medium,
|
||||
});
|
||||
|
||||
globalStyle(`${completedImagesMain} > 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",
|
||||
});
|
||||
|
||||
export const RemoveButton = style({
|
||||
marginLeft: vars.spacing.small,
|
||||
backgroundColor: vars.colors.brand,
|
||||
border: "0 none",
|
||||
padding: vars.spacing.small,
|
||||
cursor: "pointer",
|
||||
borderRadius: vars.trim.borderRadiusSmall,
|
||||
});
|
@ -0,0 +1,65 @@
|
||||
import React from "react";
|
||||
|
||||
import { CompletedImagesType } from "../index";
|
||||
|
||||
import {
|
||||
completedImagesMain,
|
||||
completedImagesList,
|
||||
imageContain,
|
||||
RemoveButton,
|
||||
// @ts-expect-error
|
||||
} from "./completedImages.css.ts";
|
||||
|
||||
interface CurrentDisplayProps {
|
||||
images: CompletedImagesType[] | null;
|
||||
setCurrentDisplay: (image: CompletedImagesType) => void;
|
||||
removeImages: () => void;
|
||||
}
|
||||
|
||||
export default function CompletedImages({
|
||||
images,
|
||||
setCurrentDisplay,
|
||||
removeImages,
|
||||
}: CurrentDisplayProps) {
|
||||
const _handleSetCurrentDisplay = (index: number) => {
|
||||
const image = images![index];
|
||||
setCurrentDisplay(image);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={completedImagesMain}>
|
||||
{/* Adjust the dom do we dont do this check twice */}
|
||||
{images != null && images.length > 0 && (
|
||||
<button
|
||||
className={RemoveButton}
|
||||
onClick={() => {
|
||||
removeImages();
|
||||
}}
|
||||
>
|
||||
REMOVE
|
||||
</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={() => {
|
||||
_handleSetCurrentDisplay(index);
|
||||
}}
|
||||
>
|
||||
<img src={image.data} alt={image.info.prompt} />
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import React from "react";
|
||||
import GeneratedImage from "../../../molecules/generatedImage";
|
||||
import { useImageCreate } from "../../../../stores/imageCreateStore";
|
||||
|
||||
import { CompletedImagesType } from "../index";
|
||||
|
||||
interface CurrentDisplayProps {
|
||||
isLoading: boolean;
|
||||
image: CompletedImagesType | null;
|
||||
}
|
||||
|
||||
export default function CurrentDisplay({
|
||||
isLoading,
|
||||
image,
|
||||
}: CurrentDisplayProps) {
|
||||
const { info, data } = image ?? {};
|
||||
|
||||
const setRequestOption = useImageCreate((state) => state.setRequestOptions);
|
||||
|
||||
const createFileName = () => {
|
||||
const {
|
||||
prompt,
|
||||
seed,
|
||||
num_inference_steps,
|
||||
guidance_scale,
|
||||
use_face_correction,
|
||||
use_upscale,
|
||||
width,
|
||||
height,
|
||||
} = info!;
|
||||
|
||||
// Most important information is the prompt
|
||||
let underscoreName = prompt.replace(/[^a-zA-Z0-9]/g, "_");
|
||||
underscoreName = underscoreName.substring(0, 100);
|
||||
// name and the top level metadata
|
||||
let fileName = `${underscoreName}_Seed-${seed}_Steps-${num_inference_steps}_Guidance-${guidance_scale}`;
|
||||
// Add the face correction and upscale
|
||||
if (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 _handleSave = () => {
|
||||
const link = document.createElement("a");
|
||||
link.download = createFileName();
|
||||
link.href = data!;
|
||||
link.click();
|
||||
};
|
||||
|
||||
const _handleUseAsInput = () => {
|
||||
setRequestOption("init_image", data);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="current-display">
|
||||
{isLoading ? (
|
||||
<h4 className="loading">Loading...</h4>
|
||||
) : (
|
||||
(image !== null && (
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
<div>
|
||||
<p> {info?.prompt}</p>
|
||||
<GeneratedImage imageData={data} metadata={info}></GeneratedImage>
|
||||
<div>
|
||||
<button onClick={_handleSave}>Save</button>
|
||||
<button onClick={_handleUseAsInput}>Use as Input</button>
|
||||
</div>
|
||||
</div>
|
||||
)) || <h4 className="no-image">Try Making a new image!</h4>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
export const displayPanel = style({
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
});
|
||||
|
||||
export const displayContainer = style({
|
||||
flexGrow: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
});
|
||||
|
||||
export const previousImages = style({});
|
@ -0,0 +1,166 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useImageQueue } from "../../../stores/imageQueueStore";
|
||||
|
||||
import { ImageRequest, useImageCreate } from "../../../stores/imageCreateStore";
|
||||
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import {
|
||||
doMakeImage,
|
||||
MakeImageKey,
|
||||
ImageReturnType,
|
||||
ImageOutput,
|
||||
} from "../../../api";
|
||||
|
||||
import AudioDing from "./audioDing";
|
||||
|
||||
// import GeneratedImage from "../../molecules/generatedImage";
|
||||
// import DrawImage from "../../molecules/drawImage";
|
||||
|
||||
import CurrentDisplay from "./currentDisplay";
|
||||
import CompletedImages from "./completedImages";
|
||||
|
||||
import {
|
||||
displayPanel,
|
||||
displayContainer,
|
||||
previousImages,
|
||||
// @ts-expect-error
|
||||
} from "./displayPanel.css.ts";
|
||||
|
||||
export interface CompletedImagesType {
|
||||
id: string;
|
||||
data: string;
|
||||
info: ImageRequest;
|
||||
}
|
||||
|
||||
const idDelim = "_batch";
|
||||
|
||||
export default function DisplayPanel() {
|
||||
const dingRef = useRef<HTMLAudioElement>(null);
|
||||
const isSoundEnabled = useImageCreate((state) => state.isSoundEnabled());
|
||||
|
||||
// @ts-expect-error
|
||||
const { id, options } = useImageQueue((state) => state.firstInQueue());
|
||||
const removeFirstInQueue = useImageQueue((state) => state.removeFirstInQueue);
|
||||
|
||||
const [currentImage, setCurrentImage] = useState<CompletedImagesType | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const [isEnabled, setIsEnabled] = useState(false);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const { status, data } = useQuery(
|
||||
[MakeImageKey, id],
|
||||
async () => await doMakeImage(options),
|
||||
{
|
||||
enabled: isEnabled,
|
||||
}
|
||||
);
|
||||
|
||||
// update the enabled state when the id changes
|
||||
useEffect(() => {
|
||||
setIsEnabled(void 0 !== id);
|
||||
}, [id]);
|
||||
|
||||
// helper for the loading state to be enabled aware
|
||||
useEffect(() => {
|
||||
if (isEnabled && status === "loading") {
|
||||
setIsLoading(true);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [isEnabled, status]);
|
||||
|
||||
// this is where there loading actually happens
|
||||
useEffect(() => {
|
||||
// query is done
|
||||
if (status === "success") {
|
||||
// check to make sure that the image was created
|
||||
if (data.status === "succeeded") {
|
||||
if (isSoundEnabled) {
|
||||
// not awaiting the promise or error handling
|
||||
void dingRef.current?.play();
|
||||
}
|
||||
removeFirstInQueue();
|
||||
}
|
||||
}
|
||||
}, [status, data, removeFirstInQueue, dingRef, isSoundEnabled]);
|
||||
|
||||
/* COMPLETED IMAGES */
|
||||
const queryClient = useQueryClient();
|
||||
const [completedImages, setCompletedImages] = useState<CompletedImagesType[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
const completedIds = useImageQueue((state) => state.completedImageIds);
|
||||
const clearCachedIds = useImageQueue((state) => state.clearCachedIds);
|
||||
|
||||
// this is where we generate the list of completed images
|
||||
useEffect(() => {
|
||||
const completedQueries = completedIds.map((id) => {
|
||||
const imageData = queryClient.getQueryData([MakeImageKey, id]);
|
||||
return imageData;
|
||||
}) as ImageReturnType[];
|
||||
|
||||
if (completedQueries.length > 0) {
|
||||
// map the completedImagesto a new array
|
||||
// and then set the state
|
||||
const temp = completedQueries
|
||||
.map((query, index) => {
|
||||
if (void 0 !== query) {
|
||||
return query.output.map((data: ImageOutput, index: number) => {
|
||||
return {
|
||||
id: `${completedIds[index]}${idDelim}-${data.seed}-${index}`,
|
||||
data: data.data,
|
||||
info: { ...query.request, seed: data.seed },
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
.flat()
|
||||
.reverse()
|
||||
.filter((item) => void 0 !== item) as CompletedImagesType[]; // remove undefined items
|
||||
|
||||
setCompletedImages(temp);
|
||||
|
||||
// could move this to the useEffect for completedImages
|
||||
if (temp.length > 0) {
|
||||
setCurrentImage(temp[0]);
|
||||
} else {
|
||||
setCurrentImage(null);
|
||||
}
|
||||
} else {
|
||||
setCompletedImages([]);
|
||||
setCurrentImage(null);
|
||||
}
|
||||
}, [setCompletedImages, setCurrentImage, queryClient, completedIds]);
|
||||
|
||||
// this is how we remove them
|
||||
const removeImages = () => {
|
||||
completedIds.forEach((id) => {
|
||||
queryClient.removeQueries([MakeImageKey, id]);
|
||||
});
|
||||
clearCachedIds();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={displayPanel}>
|
||||
<AudioDing ref={dingRef}></AudioDing>
|
||||
<div className={displayContainer}>
|
||||
<CurrentDisplay
|
||||
isLoading={isLoading}
|
||||
image={currentImage}
|
||||
></CurrentDisplay>
|
||||
</div>
|
||||
<div className={previousImages}>
|
||||
<CompletedImages
|
||||
removeImages={removeImages}
|
||||
images={completedImages}
|
||||
setCurrentDisplay={setCurrentImage}
|
||||
></CompletedImages>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
import { style, globalStyle } from "@vanilla-extract/css";
|
||||
|
||||
// @ts-expect-error
|
||||
import { vars } from "../../../styles/theme/index.css.ts";
|
||||
|
||||
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%);
|
||||
// }
|
@ -0,0 +1,71 @@
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
FooterDisplayMain,
|
||||
CoffeeButton, // @ts-expect-error
|
||||
} from "./footerDisplay.css.ts";
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { style, globalStyle } from "@vanilla-extract/css";
|
||||
//@ts-expect-error
|
||||
import { vars } from "../../../styles/theme/index.css.ts";
|
||||
|
||||
export const HeaderDisplayMain = style({
|
||||
color: vars.colors.text.normal,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
});
|
||||
|
||||
globalStyle(`${HeaderDisplayMain} > h1`, {
|
||||
fontSize: vars.fonts.sizes.Title,
|
||||
fontWeight: "bold",
|
||||
marginRight: vars.spacing.medium,
|
||||
});
|
@ -0,0 +1,52 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { KEY_CONFIG, getConfig } from "../../../api";
|
||||
|
||||
import StatusDisplay from "./statusDisplay";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import {
|
||||
HeaderDisplayMain, // @ts-expect-error
|
||||
} from "./headerDisplay.css.ts";
|
||||
|
||||
// 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}>
|
||||
<h1>
|
||||
{t("title")} {version} {release}{" "}
|
||||
</h1>
|
||||
<StatusDisplay className="status-display"></StatusDisplay>
|
||||
|
||||
{/* <LanguageDropdown></LanguageDropdown> */}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
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;
|
@ -0,0 +1,49 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { healthPing, HEALTH_PING_INTERVAL } from "../../../../api";
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
} else {
|
||||
setStatusMessage(errorMessage);
|
||||
setStatusClass(ErrorStatus);
|
||||
}
|
||||
}
|
||||
}, [status, data]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* alittle hacky but joins the class names, will probably need a better css in js solution or tailwinds */}
|
||||
<p className={[statusClass, className].join(" ")}>{statusMessage}</p>
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
|
||||
// @ts-expect-error
|
||||
import { vars } from "../../../../styles/theme/index.css.ts";
|
||||
|
||||
export const StartingStatus = style({
|
||||
color: vars.colors.warning,
|
||||
});
|
||||
|
||||
export const ErrorStatus = style({
|
||||
color: vars.colors.error,
|
||||
});
|
||||
|
||||
export const SuccessStatus = style({
|
||||
color: vars.colors.success,
|
||||
});
|
35
ui/frontend/build_src/src/main.tsx
Normal file
35
ui/frontend/build_src/src/main.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
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>
|
||||
);
|
144
ui/frontend/build_src/src/modifiers.json
Normal file
144
ui/frontend/build_src/src/modifiers.json
Normal file
@ -0,0 +1,144 @@
|
||||
[
|
||||
[
|
||||
"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"
|
||||
]
|
||||
]
|
||||
]
|
9
ui/frontend/build_src/src/pages/Experimental/index.tsx
Normal file
9
ui/frontend/build_src/src/pages/Experimental/index.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from "react";
|
||||
|
||||
export default function Beta() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Beta</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
53
ui/frontend/build_src/src/pages/Home/home.css.ts
Normal file
53
ui/frontend/build_src/src/pages/Home/home.css.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { style } from "@vanilla-extract/css";
|
||||
// @ts-expect-error
|
||||
import { vars } from "../../styles/theme/index.css.ts";
|
||||
|
||||
export const AppLayout = style({
|
||||
position: "relative",
|
||||
backgroundColor: vars.colors.background,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
pointerEvents: "auto",
|
||||
display: "grid",
|
||||
// backgroundColor: "rgb(32, 33, 36)",
|
||||
gridTemplateColumns: "400px 1fr",
|
||||
gridTemplateRows: "100px 1fr 115px",
|
||||
gridTemplateAreas: `
|
||||
"header header header"
|
||||
"create display display"
|
||||
"create footer footer"
|
||||
`,
|
||||
|
||||
"@media": {
|
||||
"screen and (max-width: 800px)": {
|
||||
gridTemplateColumns: "1fr",
|
||||
gridTemplateRows: "100px 300px 1fr 100px",
|
||||
gridTemplateAreas: `
|
||||
"header"
|
||||
"create"
|
||||
"display"
|
||||
"footer"
|
||||
`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const HeaderLayout = style({
|
||||
gridArea: "header",
|
||||
});
|
||||
|
||||
export const CreateLayout = style({
|
||||
gridArea: "create",
|
||||
position: "relative",
|
||||
});
|
||||
|
||||
export const DisplayLayout = style({
|
||||
gridArea: "display",
|
||||
overflow: "auto",
|
||||
});
|
||||
|
||||
export const FooterLayout = style({
|
||||
gridArea: "footer",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
});
|
71
ui/frontend/build_src/src/pages/Home/index.tsx
Normal file
71
ui/frontend/build_src/src/pages/Home/index.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
import {
|
||||
AppLayout,
|
||||
HeaderLayout,
|
||||
CreateLayout,
|
||||
DisplayLayout,
|
||||
FooterLayout, // @ts-expect-error
|
||||
} from "./home.css.ts";
|
||||
|
||||
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 CreationPanel from "../../components/organisms/creationPanel";
|
||||
import DisplayPanel from "../../components/organisms/displayPanel";
|
||||
import FooterDisplay from "../../components/organisms/footerDisplay";
|
||||
|
||||
function Home({ className }: { className: any }) {
|
||||
// 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, className].join(" ")}>
|
||||
<header className={HeaderLayout}>
|
||||
<HeaderDisplay></HeaderDisplay>
|
||||
</header>
|
||||
<nav className={CreateLayout}>
|
||||
<CreationPanel></CreationPanel>
|
||||
</nav>
|
||||
<main className={DisplayLayout}>
|
||||
<DisplayPanel></DisplayPanel>
|
||||
</main>
|
||||
<footer className={FooterLayout}>
|
||||
<FooterDisplay></FooterDisplay>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
9
ui/frontend/build_src/src/pages/Settings/index.tsx
Normal file
9
ui/frontend/build_src/src/pages/Settings/index.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from "react";
|
||||
|
||||
export default function Settings({ className }: { className: any }) {
|
||||
return (
|
||||
<div>
|
||||
<h1>Settings</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
318
ui/frontend/build_src/src/stores/imageCreateStore.ts
Normal file
318
ui/frontend/build_src/src/stores/imageCreateStore.ts
Normal file
@ -0,0 +1,318 @@
|
||||
import create from "zustand";
|
||||
import produce from "immer";
|
||||
import { devtools } from "zustand/middleware";
|
||||
|
||||
import { useRandomSeed } from "../utils";
|
||||
|
||||
export interface ImageCreationUiOptions {
|
||||
isUseRandomSeed: boolean;
|
||||
isUseAutoSave: boolean;
|
||||
isSoundEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface ImageRequest {
|
||||
prompt: string;
|
||||
seed: number;
|
||||
num_outputs: number;
|
||||
num_inference_steps: number;
|
||||
guidance_scale: number;
|
||||
width:
|
||||
| 128
|
||||
| 192
|
||||
| 256
|
||||
| 320
|
||||
| 384
|
||||
| 448
|
||||
| 512
|
||||
| 576
|
||||
| 640
|
||||
| 704
|
||||
| 768
|
||||
| 832
|
||||
| 896
|
||||
| 960
|
||||
| 1024;
|
||||
height:
|
||||
| 128
|
||||
| 192
|
||||
| 256
|
||||
| 320
|
||||
| 384
|
||||
| 448
|
||||
| 512
|
||||
| 576
|
||||
| 640
|
||||
| 704
|
||||
| 768
|
||||
| 832
|
||||
| 896
|
||||
| 960
|
||||
| 1024;
|
||||
// allow_nsfw: boolean
|
||||
turbo: boolean;
|
||||
use_cpu: boolean;
|
||||
use_full_precision: boolean;
|
||||
save_to_disk_path: null | string;
|
||||
use_face_correction: null | "GFPGANv1.3";
|
||||
use_upscale: null | "RealESRGAN_x4plus" | "RealESRGAN_x4plus_anime_6B" | "";
|
||||
show_only_filtered_image: boolean;
|
||||
init_image: undefined | string;
|
||||
prompt_strength: undefined | number;
|
||||
}
|
||||
|
||||
type ModifiersList = string[];
|
||||
type ModifiersOptions = string | ModifiersList[];
|
||||
type ModifiersOptionList = ModifiersOptions[];
|
||||
|
||||
interface ImageCreateState {
|
||||
parallelCount: number;
|
||||
requestOptions: ImageRequest;
|
||||
allModifiers: ModifiersOptionList;
|
||||
tags: 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: (tag: string) => void;
|
||||
hasTag: (tag: string) => boolean;
|
||||
selectedTags: () => string[];
|
||||
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: {
|
||||
prompt: "a photograph of an astronaut riding a horse",
|
||||
seed: useRandomSeed(),
|
||||
num_outputs: 1,
|
||||
num_inference_steps: 50,
|
||||
guidance_scale: 7.5,
|
||||
width: 512,
|
||||
height: 512,
|
||||
prompt_strength: 0.8,
|
||||
// allow_nsfw: false,
|
||||
turbo: true,
|
||||
use_cpu: false,
|
||||
use_full_precision: true,
|
||||
save_to_disk_path: "null",
|
||||
use_face_correction: "GFPGANv1.3",
|
||||
use_upscale: "RealESRGAN_x4plus",
|
||||
show_only_filtered_image: true,
|
||||
init_image: undefined,
|
||||
},
|
||||
|
||||
// selected tags
|
||||
tags: [] as 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: (tag: string) => {
|
||||
set(
|
||||
produce((state) => {
|
||||
const index = state.tags.indexOf(tag);
|
||||
if (index > -1) {
|
||||
state.tags.splice(index, 1);
|
||||
} else {
|
||||
state.tags.push(tag);
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
hasTag: (tag: string) => {
|
||||
return get().tags.includes(tag);
|
||||
},
|
||||
|
||||
selectedTags: () => {
|
||||
return get().tags;
|
||||
},
|
||||
|
||||
// the request body to send to the server
|
||||
// this is a computed value, just adding the tags to the request
|
||||
builtRequest: () => {
|
||||
const state = get();
|
||||
const requestOptions = state.requestOptions;
|
||||
const tags = state.tags;
|
||||
|
||||
// join all the tags with a comma and add it to the prompt
|
||||
const prompt = `${requestOptions.prompt} ${tags.join(",")}`;
|
||||
|
||||
const request = {
|
||||
...requestOptions,
|
||||
prompt,
|
||||
};
|
||||
// if we arent using auto save clear the save path
|
||||
if (!state.uiOptions.isUseAutoSave) {
|
||||
// maybe this is "None" ?
|
||||
// TODO check this
|
||||
request.save_to_disk_path = null;
|
||||
}
|
||||
|
||||
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;
|
||||
//localStorage.setItem('ui:isSoundEnabled', state.uiOptions.isSoundEnabled);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
isSoundEnabled: () => {
|
||||
return get().uiOptions.isSoundEnabled;
|
||||
},
|
||||
|
||||
toggleInpainting: () => {
|
||||
set(
|
||||
produce((state: ImageCreateState) => {
|
||||
state.isInpainting = !state.isInpainting;
|
||||
})
|
||||
);
|
||||
},
|
||||
}))
|
||||
);
|
22
ui/frontend/build_src/src/stores/imageDisplayStore.ts
Normal file
22
ui/frontend/build_src/src/stores/imageDisplayStore.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import create from "zustand";
|
||||
import produce from "immer";
|
||||
|
||||
interface ImageDisplayState {
|
||||
imageOptions: Map<string, any>;
|
||||
currentImage: object | null;
|
||||
addNewImage: (ImageData: string, imageOptions: any) => void;
|
||||
}
|
||||
|
||||
export const useImageDisplay = create<ImageDisplayState>((set) => ({
|
||||
imageOptions: new Map<string, any>(),
|
||||
currentImage: null,
|
||||
// use produce to make sure we don't mutate state
|
||||
addNewImage: (ImageData: string, imageOptions: any) => {
|
||||
set(
|
||||
produce((state) => {
|
||||
state.currentImage = { display: ImageData, options: imageOptions };
|
||||
state.images.set(ImageData, imageOptions);
|
||||
})
|
||||
);
|
||||
},
|
||||
}));
|
59
ui/frontend/build_src/src/stores/imageQueueStore.ts
Normal file
59
ui/frontend/build_src/src/stores/imageQueueStore.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import create from "zustand";
|
||||
import produce from "immer";
|
||||
import { useRandomSeed } from "../utils";
|
||||
|
||||
import { ImageRequest } from "./imageCreateStore";
|
||||
|
||||
interface ImageQueueState {
|
||||
images: ImageRequest[];
|
||||
completedImageIds: string[];
|
||||
addNewImage: (id: string, imgRec: ImageRequest) => void;
|
||||
hasQueuedImages: () => boolean;
|
||||
firstInQueue: () => ImageRequest | {};
|
||||
removeFirstInQueue: () => void;
|
||||
clearCachedIds: () => void;
|
||||
}
|
||||
|
||||
export const useImageQueue = create<ImageQueueState>((set, get) => ({
|
||||
images: [],
|
||||
completedImageIds: [],
|
||||
// use produce to make sure we don't mutate state
|
||||
addNewImage: (id: string, imgRec: ImageRequest, isRandom = false) => {
|
||||
set(
|
||||
produce((state) => {
|
||||
let { seed } = imgRec;
|
||||
if (isRandom) {
|
||||
seed = useRandomSeed();
|
||||
}
|
||||
state.images.push({ id, options: { ...imgRec, seed } });
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
hasQueuedImages: () => {
|
||||
return get().images.length > 0;
|
||||
},
|
||||
|
||||
firstInQueue: () => {
|
||||
let first: ImageRequest | {} = get().images[0];
|
||||
first = void 0 !== first ? first : {};
|
||||
return first;
|
||||
},
|
||||
|
||||
removeFirstInQueue: () => {
|
||||
set(
|
||||
produce((state) => {
|
||||
const image = state.images.shift();
|
||||
state.completedImageIds.push(image.id);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
clearCachedIds: () => {
|
||||
set(
|
||||
produce((state) => {
|
||||
state.completedImageIds = [];
|
||||
})
|
||||
);
|
||||
},
|
||||
}));
|
58
ui/frontend/build_src/src/styles/index.css.ts
Normal file
58
ui/frontend/build_src/src/styles/index.css.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { globalStyle } from "@vanilla-extract/css";
|
||||
// @ts-expect-error
|
||||
import { vars } from "./theme/index.css.ts";
|
||||
|
||||
// baisc body style
|
||||
globalStyle("body", {
|
||||
margin: 0,
|
||||
minWidth: "320px",
|
||||
minHeight: "100vh",
|
||||
});
|
||||
|
||||
// single page style
|
||||
globalStyle("#root", {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
overflow: "hidden",
|
||||
});
|
||||
|
||||
// border box all
|
||||
globalStyle(`*`, {
|
||||
boxSizing: "border-box",
|
||||
});
|
||||
|
||||
globalStyle(`button`, {
|
||||
fontSize: vars.fonts.sizes.Body,
|
||||
});
|
||||
|
||||
/** RESETS */
|
||||
globalStyle(`p, h1, h2, h3, h4, h5, h6, ul`, {
|
||||
margin: 0,
|
||||
});
|
||||
|
||||
globalStyle(`h3`, {
|
||||
fontSize: vars.fonts.sizes.Subheadline,
|
||||
fontFamily: vars.fonts.body,
|
||||
});
|
||||
|
||||
globalStyle(`h4, h5`, {
|
||||
fontSize: vars.fonts.sizes.SubSubheadline,
|
||||
fontFamily: vars.fonts.body,
|
||||
});
|
||||
|
||||
globalStyle(`p, label`, {
|
||||
fontSize: vars.fonts.sizes.Body,
|
||||
fontFamily: vars.fonts.body,
|
||||
});
|
||||
|
||||
globalStyle(`textarea`, {
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
border: "none",
|
||||
fontSize: vars.fonts.sizes.Body,
|
||||
fontWeight: "bold",
|
||||
fontFamily: vars.fonts.body,
|
||||
});
|
22
ui/frontend/build_src/src/styles/recipes/index.css.ts
Normal file
22
ui/frontend/build_src/src/styles/recipes/index.css.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { recipe } from "@vanilla-extract/recipes";
|
||||
|
||||
export const button = recipe({
|
||||
variants: {
|
||||
color: {
|
||||
neutral: { background: "whitesmoke" },
|
||||
brand: { background: "blueviolet" },
|
||||
accent: { background: "slateblue" },
|
||||
},
|
||||
size: {
|
||||
small: { padding: 12 },
|
||||
medium: { padding: 16 },
|
||||
large: { padding: 24 },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// export const card = recipe({
|
||||
// variants: {
|
||||
// color: {
|
||||
|
||||
// alt: { background: 'whitesmoke' },
|
29
ui/frontend/build_src/src/styles/shared.css.ts
Normal file
29
ui/frontend/build_src/src/styles/shared.css.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { style, globalStyle } from "@vanilla-extract/css";
|
||||
// @ts-expect-error
|
||||
import { vars } from "./theme/index.css.ts";
|
||||
|
||||
export const PanelBox = style({
|
||||
background: vars.colors.backgroundAlt,
|
||||
color: vars.colors.text.normal,
|
||||
padding: vars.spacing.medium,
|
||||
borderRadius: vars.trim.smallBorderRadius,
|
||||
marginBottom: vars.spacing.medium,
|
||||
// TODO move this to the theme
|
||||
boxShadow:
|
||||
"0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15)",
|
||||
});
|
||||
|
||||
globalStyle(`${PanelBox} .panel-box-toggle-btn`, {
|
||||
display: "block",
|
||||
width: "100%",
|
||||
textAlign: "left",
|
||||
backgroundColor: "transparent",
|
||||
color: vars.colors.text.normal,
|
||||
border: "0 none",
|
||||
cursor: "pointer",
|
||||
padding: "0",
|
||||
});
|
||||
|
||||
export const SettingItem = style({
|
||||
marginBottom: vars.spacing.medium,
|
||||
});
|
161
ui/frontend/build_src/src/styles/theme/index.css.ts
Normal file
161
ui/frontend/build_src/src/styles/theme/index.css.ts
Normal file
@ -0,0 +1,161 @@
|
||||
import {
|
||||
createGlobalTheme,
|
||||
createThemeContract,
|
||||
createTheme,
|
||||
} from "@vanilla-extract/css";
|
||||
|
||||
/**
|
||||
* Colors are all the same across the themes, this is just to set up a contract
|
||||
* Colors can be decided later. I am just the architect.
|
||||
* Tried to pull things from the original app.
|
||||
*
|
||||
* Lots of these arent used yet, but once they are defined and useable then they can be set.
|
||||
*/
|
||||
|
||||
const colors = createThemeContract({
|
||||
brand: null,
|
||||
brandDimmed: null,
|
||||
brandHover: null,
|
||||
brandActive: null,
|
||||
brandAccent: null,
|
||||
brandAccentDimmed: null,
|
||||
brandAccentActive: null,
|
||||
|
||||
secondary: null,
|
||||
secondaryDimmed: null,
|
||||
secondaryHover: null,
|
||||
secondaryActive: null,
|
||||
secondaryAccent: null,
|
||||
secondaryAccentDimmed: null,
|
||||
secondaryAccentActive: null,
|
||||
|
||||
background: null,
|
||||
backgroundAccent: null,
|
||||
backgroundAlt: null,
|
||||
backgroundAltAccent: null,
|
||||
|
||||
text: {
|
||||
normal: null,
|
||||
dimmed: null,
|
||||
|
||||
secondary: null,
|
||||
secondaryDimmed: null,
|
||||
|
||||
accent: null,
|
||||
accentDimmed: null,
|
||||
},
|
||||
|
||||
link: null,
|
||||
warning: null,
|
||||
error: null,
|
||||
success: null,
|
||||
});
|
||||
|
||||
const app = createGlobalTheme(":root", {
|
||||
spacing: {
|
||||
none: "0",
|
||||
min: "2px",
|
||||
small: "5px",
|
||||
medium: "10px",
|
||||
large: "25px",
|
||||
},
|
||||
|
||||
trim: {
|
||||
smallBorderRadius: "5px",
|
||||
},
|
||||
|
||||
fonts: {
|
||||
body: "Arial, Helvetica, sans-serif;",
|
||||
sizes: {
|
||||
Title: "2em",
|
||||
Headline: "1.5em",
|
||||
Subheadline: "1.20em",
|
||||
SubSubheadline: "1.1em",
|
||||
Body: "1em",
|
||||
Plain: "0.8em",
|
||||
Caption: ".75em",
|
||||
Overline: ".5em",
|
||||
},
|
||||
},
|
||||
colors,
|
||||
});
|
||||
|
||||
export const darkTheme = createTheme(colors, {
|
||||
brand: "#5000b9", // purple
|
||||
brandDimmed: "#433852", // muted purple
|
||||
brandHover: "#5d00d6", // bringhter purple
|
||||
brandActive: "#5d00d6", // bringhter purple
|
||||
brandAccent: "#28004e", // darker purple
|
||||
brandAccentDimmed: "#28004e", // darker purple
|
||||
brandAccentActive: "#28004e", // darker purple
|
||||
|
||||
secondary: "#0b8334", // green
|
||||
secondaryDimmed: "#0b8334", // green
|
||||
secondaryHover: "#0b8334", // green
|
||||
secondaryActive: "#0b8334", // green
|
||||
secondaryAccent: "#0b8334", // green
|
||||
secondaryAccentDimmed: "#0b8334", // green
|
||||
secondaryAccentActive: "#0b8334", // green
|
||||
|
||||
background: "#202124", // dark grey
|
||||
backgroundAccent: " #383838", // lighter grey
|
||||
backgroundAlt: "#2c2d30", // med grey
|
||||
backgroundAltAccent: "#383838", // lighter grey
|
||||
|
||||
text: {
|
||||
normal: "#ffffff", // white
|
||||
dimmed: "#d1d5db", // off white
|
||||
|
||||
secondary: "#ffffff", // white
|
||||
secondaryDimmed: "#d1d5db", // off white
|
||||
|
||||
accent: "#e7ba71", // orange
|
||||
accentDimmed: "#7d6641", // muted orange
|
||||
},
|
||||
|
||||
link: "#0066cc", // blue
|
||||
warning: "#f0ad4e",
|
||||
error: "#d9534f",
|
||||
success: "#5cb85c",
|
||||
});
|
||||
|
||||
// Generated by co-pilot
|
||||
export const lightTheme = createTheme(colors, {
|
||||
brand: "#1E40AF",
|
||||
brandDimmed: "#1E40AF",
|
||||
brandHover: "#1E40AF",
|
||||
brandActive: "#1E40AF",
|
||||
brandAccent: "#1E40AF",
|
||||
brandAccentDimmed: "#1E40AF",
|
||||
brandAccentActive: "#1E40AF",
|
||||
|
||||
secondary: "#DB2777",
|
||||
secondaryDimmed: "#DB2777",
|
||||
secondaryHover: "#DB2777",
|
||||
secondaryActive: "#DB2777",
|
||||
secondaryAccent: "#DB2777",
|
||||
secondaryAccentDimmed: "#DB2777",
|
||||
secondaryAccentActive: "#DB2777",
|
||||
|
||||
background: "#EFF6FF",
|
||||
backgroundAccent: "#EFF6FF",
|
||||
backgroundAlt: "#EFF6FF",
|
||||
backgroundAltAccent: "#EFF6FF",
|
||||
text: {
|
||||
normal: "#1F2937",
|
||||
dimmed: "#6B7280",
|
||||
|
||||
secondary: "#1F2937",
|
||||
secondaryDimmed: "#6B7280",
|
||||
|
||||
accent: "#1F2937",
|
||||
accentDimmed: "#6B7280",
|
||||
},
|
||||
|
||||
link: "#0066cc", // blue
|
||||
warning: "yellow",
|
||||
error: "red",
|
||||
success: "green",
|
||||
});
|
||||
|
||||
export const vars = { ...app, colors };
|
3
ui/frontend/build_src/src/utils.ts
Normal file
3
ui/frontend/build_src/src/utils.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function useRandomSeed() {
|
||||
return Math.floor(Math.random() * 10000);
|
||||
}
|
1
ui/frontend/build_src/src/vite-env.d.ts
vendored
Normal file
1
ui/frontend/build_src/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
26
ui/frontend/build_src/tsconfig.json
Normal file
26
ui/frontend/build_src/tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@stores": ["src/stores"]
|
||||
},
|
||||
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
9
ui/frontend/build_src/tsconfig.node.json
Normal file
9
ui/frontend/build_src/tsconfig.node.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
42
ui/frontend/build_src/vite.config.ts
Normal file
42
ui/frontend/build_src/vite.config.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { defineConfig } from "vite";
|
||||
import eslint from "vite-plugin-eslint";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
|
||||
|
||||
import path from "path";
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
// TODO figure out why vs code complains about this even though it works
|
||||
"@stores": path.resolve(__dirname, "./src/stores"),
|
||||
// TODO - add more aliases
|
||||
},
|
||||
},
|
||||
|
||||
plugins: [
|
||||
eslint(),
|
||||
react(),
|
||||
vanillaExtractPlugin({
|
||||
// configuration
|
||||
}),
|
||||
],
|
||||
|
||||
server: {
|
||||
port: 9001,
|
||||
},
|
||||
|
||||
build: {
|
||||
// make sure everythign is in the same directory
|
||||
outDir: "../dist",
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// dont hash the file names
|
||||
// maybe once we update the python server?
|
||||
entryFileNames: `[name].js`,
|
||||
chunkFileNames: `[name].js`,
|
||||
assetFileNames: `[name].[ext]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
1
ui/frontend/dist/index.css
vendored
Normal file
1
ui/frontend/dist/index.css
vendored
Normal file
File diff suppressed because one or more lines are too long
16
ui/frontend/dist/index.html
vendored
Normal file
16
ui/frontend/dist/index.html
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="color-scheme" content="dark light" />
|
||||
<title>Stable Diffusion UI</title>
|
||||
<script type="module" crossorigin src="/index.js"></script>
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- The react app entry point. Currently no ui just poc importing and logging -->
|
||||
<div id="root"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
110
ui/frontend/dist/index.js
vendored
Normal file
110
ui/frontend/dist/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,3 +1,7 @@
|
||||
<!--
|
||||
THIS IS NO LONGER BEING SERVED REMOVE WHEN WE HAVE PORTED ANY
|
||||
OF THE FEATURES THAT WERE PARTIALLY COMPLETE WHEN WE DID THE CUT OVER
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@ -1269,7 +1273,6 @@ function showInitImagePreview() {
|
||||
let file = initImageSelector.files[0]
|
||||
|
||||
reader.addEventListener('load', function() {
|
||||
// console.log(file.name, reader.result)
|
||||
initImagePreview.src = reader.result
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
inpaintingEditorContainer.style.display = 'none'
|
||||
|
39
ui/server.py
39
ui/server.py
@ -18,12 +18,26 @@ from fastapi import FastAPI, HTTPException
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.responses import FileResponse, StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
# this is needed for development.
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import logging
|
||||
|
||||
from sd_internal import Request, Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# we need to be able to run a local server for the UI (9001)
|
||||
# and still be able to hit our python port (9000)
|
||||
origins = ["*"]
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
model_loaded = False
|
||||
model_is_loading = False
|
||||
|
||||
@ -63,7 +77,18 @@ app.mount('/media', StaticFiles(directory=os.path.join(SD_UI_DIR, 'media/')), na
|
||||
@app.get('/')
|
||||
def read_root():
|
||||
headers = {"Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0"}
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'index.html'), headers=headers)
|
||||
return FileResponse(os.path.join(SD_UI_DIR,'frontend/dist/index.html'), headers=headers)
|
||||
|
||||
# then get the js files
|
||||
@app.get('/index.js')
|
||||
def read_scripts():
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'frontend/dist/index.js'))
|
||||
|
||||
#then get the css files
|
||||
@app.get('/index.css')
|
||||
def read_styles():
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'frontend/dist/index.css'))
|
||||
|
||||
|
||||
@app.get('/ping')
|
||||
async def ping():
|
||||
@ -194,9 +219,19 @@ def getAppConfig():
|
||||
print(traceback.format_exc())
|
||||
return HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
# moved these to the root for easier pathing
|
||||
# TODO: change the vite config for public files
|
||||
@app.get('/ding.mp3')
|
||||
def read_ding():
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'frontend/assets/ding.mp3'))
|
||||
|
||||
@app.get('/kofi.png')
|
||||
def read_kofi():
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'frontend/assets/kofi.png'))
|
||||
|
||||
@app.get('/modifiers.json')
|
||||
def read_modifiers():
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'modifiers.json'))
|
||||
return FileResponse(os.path.join(SD_UI_DIR, 'frontend/assets/modifiers.json'))
|
||||
|
||||
@app.get('/output_dir')
|
||||
def read_home_dir():
|
||||
|
Loading…
Reference in New Issue
Block a user