mirror of
https://github.com/easydiffusion/easydiffusion.git
synced 2024-11-26 02:05:09 +01:00
Merge branch 'beta' into refactor
This commit is contained in:
commit
c2bcf89f9a
@ -27,6 +27,10 @@
|
||||
- Support loading models in the safetensor format, for improved safety
|
||||
|
||||
### Detailed changelog
|
||||
* 2.4.20 - 22 Dec 2022 - `Pause All` button to pause all the pending tasks. Thanks @JeLuf
|
||||
* 2.4.20 - 22 Dec 2022 - `Undo`/`Redo` buttons in the image editor. Thanks @JeLuf
|
||||
* 2.4.20 - 22 Dec 2022 - Drag handle to reorder the tasks. This fixed a bug where the metadata was no longer selectable (for copying). Thanks @JeLuf
|
||||
* 2.4.19 - 17 Dec 2022 - Add Undo/Redo buttons in the Image Editor. Thanks @JeLuf
|
||||
* 2.4.19 - 10 Dec 2022 - Show init img in task list
|
||||
* 2.4.19 - 7 Dec 2022 - Use pre-trained hypernetworks while generating images. Thanks @C0bra5
|
||||
* 2.4.19 - 6 Dec 2022 - Allow processing new tasks first. Thanks @madrang
|
||||
|
@ -6,7 +6,7 @@ Thanks
|
||||
|
||||
# For developers:
|
||||
|
||||
If you would like to contribute to this project, there is a discord for dicussion:
|
||||
If you would like to contribute to this project, there is a discord for discussion:
|
||||
[![Discord Server](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.com/invite/u9yhsFmEkB)
|
||||
|
||||
## Development environment for UI (frontend and server) changes
|
||||
|
139
README.md
139
README.md
@ -1,70 +1,107 @@
|
||||
# Stable Diffusion UI
|
||||
### Easiest way to install and use [Stable Diffusion](https://github.com/CompVis/stable-diffusion) on your own computer. No dependencies or technical knowledge required. 1-click install, powerful features.
|
||||
### The easiest way to install and use [Stable Diffusion](https://github.com/CompVis/stable-diffusion) on your own computer. Does not require technical knowledge, does not require pre-installed software. 1-click install, powerful features, friendly community.
|
||||
|
||||
[![Discord Server](https://img.shields.io/discord/1014774730907209781?label=Discord)](https://discord.com/invite/u9yhsFmEkB) (for support, and development discussion) | [Troubleshooting guide for common problems](Troubleshooting.md)
|
||||
[![Discord Server](https://img.shields.io/discord/1014774730907209781?label=Discord)](https://discord.com/invite/u9yhsFmEkB) (for support, and development discussion) | [Troubleshooting guide for common problems](https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting)
|
||||
|
||||
New! Experimental support for Stable Diffusion 2.0 is available in beta!
|
||||
### New:
|
||||
Experimental support for Stable Diffusion 2.0 is available in beta!
|
||||
|
||||
----
|
||||
|
||||
## Step 1: Download the installer
|
||||
# Step 1: Download and prepare the installer
|
||||
Click the download button for your operating system:
|
||||
|
||||
<p float="left">
|
||||
<a href="#installation"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/develop/media/download-win.png" width="200" /></a>
|
||||
<a href="#installation"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/develop/media/download-linux.png" width="200" /></a>
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.4.13/stable-diffusion-ui-windows.zip"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/download-win.png" width="200" /></a>
|
||||
<a href="https://github.com/cmdr2/stable-diffusion-ui#installation"><img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/download-linux.png" width="200" /></a>
|
||||
</p>
|
||||
|
||||
## Step 2: Run the program
|
||||
- On Windows: Double-click `Start Stable Diffusion UI.cmd`
|
||||
- On Linux: Run `./start.sh` in a terminal
|
||||
## On Windows:
|
||||
1. Unzip/extract the folder `stable-diffusion-ui` which should be in your downloads folder, unless you changed your default downloads destination.
|
||||
2. Move the `stable-diffusion-ui` folder to your `C:` drive (or any other drive like `D:`, at the top root level). `C:\stable-diffusion-ui` or `D:\stable-diffusion-ui` as examples. This will avoid a common problem with Windows (file path length limits).
|
||||
## On Linux:
|
||||
1. Unzip/extract the folder `stable-diffusion-ui` which should be in your downloads folder, unless you changed your default downloads destination.
|
||||
2. Open a terminal window, and navigate to the `stable-diffusion-ui` directory.
|
||||
|
||||
## Step 3: There is no step 3!
|
||||
It's simple to get started. You don't need to install or struggle with Python, Anaconda, Docker etc.
|
||||
# Step 2: Run the program
|
||||
## On Windows:
|
||||
Double-click `Start Stable Diffusion UI.cmd`.
|
||||
If Windows SmartScreen prevents you from running the program click `More info` and then `Run anyway`.
|
||||
## On Linux:
|
||||
Run `./start.sh` (or `bash start.sh`) in a terminal.
|
||||
|
||||
The installer will take care of whatever is needed. A friendly [Discord community](https://discord.com/invite/u9yhsFmEkB) will help you if you face any problems.
|
||||
The installer will take care of whatever is needed. If you face any problems, you can join the friendly [Discord community](https://discord.com/invite/u9yhsFmEkB) and ask for assistance.
|
||||
|
||||
# Step 3: There is no Step 3. It's that simple!
|
||||
|
||||
**To Uninstall:** Just delete the `stable-diffusion-ui` folder to uninstall all the downloaded packages.
|
||||
|
||||
----
|
||||
|
||||
# Easy for new users, powerful features for advanced users
|
||||
### Features:
|
||||
- **No Dependencies or Technical Knowledge Required**: 1-click install for Windows 10/11 and Linux. *No dependencies*, no need for WSL or Docker or Conda or technical setup. Just download and run!
|
||||
- **Clutter-free UI**: a friendly and simple UI, while providing a lot of powerful features
|
||||
- Supports "*Text to Image*" and "*Image to Image*"
|
||||
- **Stable Diffusion 2.0 support (experimental)** - available in beta channel
|
||||
- **Custom Models**: Use your own `.ckpt` file, by placing it inside the `models/stable-diffusion` folder!
|
||||
- **Auto scan for malicious models** - uses picklescan to prevent malicious models
|
||||
- **Live Preview**: See the image as the AI is drawing it
|
||||
- **Task Queue**: Queue up all your ideas, without waiting for the current task to finish
|
||||
- **In-Painting**: Specify areas of your image to paint into
|
||||
- **Face Correction (GFPGAN) and Upscaling (RealESRGAN)**
|
||||
- **Image Modifiers**: A library of *modifier tags* like *"Realistic"*, *"Pencil Sketch"*, *"ArtStation"* etc. Experiment with various styles quickly.
|
||||
- **Loopback**: Use the output image as the input image for the next img2img task
|
||||
## Features:
|
||||
|
||||
### User experience
|
||||
- **Hassle-free installation**: Does not require technical knowledge, does not require pre-installed software. Just download and run!
|
||||
- **Clutter-free UI**: A friendly and simple UI, while providing a lot of powerful features.
|
||||
|
||||
### Image generation
|
||||
- **Supports**: "*Text to Image*" and "*Image to Image*".
|
||||
- **In-Painting**: Specify areas of your image to paint into.
|
||||
- **Simple Drawing Tool**: Draw basic images to guide the AI, without needing an external drawing program.
|
||||
- **Face Correction (GFPGAN)**
|
||||
- **Upscaling (RealESRGAN)**
|
||||
- **Loopback**: Use the output image as the input image for the next img2img task.
|
||||
- **Negative Prompt**: Specify aspects of the image to *remove*.
|
||||
- **Attention/Emphasis:** () in the prompt increases the model's attention to enclosed words, and [] decreases it
|
||||
- **Weighted Prompts:** Use weights for specific words in your prompt to change their importance, e.g. `red:2.4 dragon:1.2`
|
||||
- **Prompt Matrix:** (in beta) Quickly create multiple variations of your prompt, e.g. `a photograph of an astronaut riding a horse | illustration | cinematic lighting`
|
||||
- **Lots of Samplers:** ddim, plms, heun, euler, euler_a, dpm2, dpm2_a, lms
|
||||
- **Multiple Prompts File:** Queue multiple prompts by entering one prompt per line, or by running a text file
|
||||
- **NSFW Setting**: A setting in the UI to control *NSFW content*
|
||||
- **JPEG/PNG output**
|
||||
- **Save generated images to disk**
|
||||
- **Attention/Emphasis**: () in the prompt increases the model's attention to enclosed words, and [] decreases it.
|
||||
- **Weighted Prompts**: Use weights for specific words in your prompt to change their importance, e.g. `red:2.4 dragon:1.2`.
|
||||
- **Prompt Matrix**: Quickly create multiple variations of your prompt, e.g. `a photograph of an astronaut riding a horse | illustration | cinematic lighting`.
|
||||
- **Lots of Samplers**: ddim, plms, heun, euler, euler_a, dpm2, dpm2_a, lms.
|
||||
- **1-click Upscale/Face Correction**: Upscale or correct an image after it has been generated.
|
||||
- **Make Similar Images**: Click to generate multiple variations of a generated image.
|
||||
- **NSFW Setting**: A setting in the UI to control *NSFW content*.
|
||||
- **JPEG/PNG output**: Multiple file formats.
|
||||
|
||||
### Advanced features
|
||||
- **Custom Models**: Use your own `.ckpt` or `.safetensors` file, by placing it inside the `models/stable-diffusion` folder!
|
||||
- **Stable Diffusion 2.0 support (experimental)**: available in beta channel.
|
||||
- **Use custom VAE models**
|
||||
- **Use pre-trained Hypernetworks**
|
||||
- **UI Plugins**: Choose from a growing list of [community-generated UI plugins](https://github.com/cmdr2/stable-diffusion-ui/wiki/UI-Plugins), or write your own plugin to add features to the project!
|
||||
|
||||
### Performance and security
|
||||
- **Low Memory Usage**: Creates 512x512 images with less than 4GB of GPU RAM!
|
||||
- **Use CPU setting**: If you don't have a compatible graphics card, but still want to run it on your CPU.
|
||||
- **Multi-GPU support**: Automatically spreads your tasks across multiple GPUs (if available), for faster performance!
|
||||
- **Auto scan for malicious models**: Uses picklescan to prevent malicious models.
|
||||
- **Safetensors support**: Support loading models in the safetensor format, for improved safety.
|
||||
- **Auto-updater**: Gets you the latest improvements and bug-fixes to a rapidly evolving project.
|
||||
- **Low Memory Usage**: Creates 512x512 images with less than 4GB of VRAM!
|
||||
- **Developer Console**: A developer-mode for those who want to modify their Stable Diffusion code, and edit the conda environment.
|
||||
|
||||
### Easy for new users:
|
||||
### Usability:
|
||||
- **Live Preview**: See the image as the AI is drawing it.
|
||||
- **Task Queue**: Queue up all your ideas, without waiting for the current task to finish.
|
||||
- **Image Modifiers**: A library of *modifier tags* like *"Realistic"*, *"Pencil Sketch"*, *"ArtStation"* etc. Experiment with various styles quickly.
|
||||
- **Multiple Prompts File**: Queue multiple prompts by entering one prompt per line, or by running a text file.
|
||||
- **Save generated images to disk**: Save your images to your PC!
|
||||
- **UI Themes**: Customize the program to your liking.
|
||||
|
||||
**(and a lot more)**
|
||||
|
||||
----
|
||||
|
||||
## Easy for new users:
|
||||
![Screenshot of the initial UI](media/shot-v10-simple.jpg?raw=true)
|
||||
|
||||
### Powerful features for advanced users:
|
||||
## Powerful features for advanced users:
|
||||
![Screenshot of advanced settings](media/shot-v10.jpg?raw=true)
|
||||
|
||||
### Live Preview
|
||||
## Live Preview
|
||||
Useful for judging (and stopping) an image quickly, without waiting for it to finish rendering.
|
||||
|
||||
![live-512](https://user-images.githubusercontent.com/844287/192097249-729a0a1e-a677-485e-9ccc-16a9e848fabe.gif)
|
||||
|
||||
### Task Queue
|
||||
## Task Queue
|
||||
![Screenshot of task queue](media/task-queue-v1.jpg?raw=true)
|
||||
|
||||
# System Requirements
|
||||
@ -74,23 +111,10 @@ Useful for judging (and stopping) an image quickly, without waiting for it to fi
|
||||
|
||||
You don't need to install or struggle with Python, Anaconda, Docker etc. The installer will take care of whatever is needed.
|
||||
|
||||
# Installation
|
||||
1. **Download** [for Windows](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.4.13/stable-diffusion-ui-windows.zip) or [for Linux](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.4.13/stable-diffusion-ui-linux.zip).
|
||||
|
||||
2. **Extract**:
|
||||
- For Windows: After unzipping the file, please move the `stable-diffusion-ui` folder to your `C:` (or any drive like D:, at the top root level), e.g. `C:\stable-diffusion-ui`. This will avoid a common problem with Windows (file path length limits).
|
||||
- For Linux: After extracting the .tar.xz file, please open a terminal, and go to the `stable-diffusion-ui` directory.
|
||||
|
||||
3. **Run**:
|
||||
- For Windows: `Start Stable Diffusion UI.cmd` by double-clicking it.
|
||||
- For Linux: In the terminal, run `./start.sh` (or `bash start.sh`)
|
||||
|
||||
This will automatically install Stable Diffusion, set it up, and start the interface. No additional steps are needed.
|
||||
|
||||
**To Uninstall:** Just delete the `stable-diffusion-ui` folder to uninstall all the downloaded packages.
|
||||
----
|
||||
|
||||
# How to use?
|
||||
Please use our [guide](https://github.com/cmdr2/stable-diffusion-ui/wiki/How-to-Use) to understand how to use the features in this UI.
|
||||
Please refer to our [guide](https://github.com/cmdr2/stable-diffusion-ui/wiki/How-to-Use) to understand how to use the features in this UI.
|
||||
|
||||
# Bugs reports and code contributions welcome
|
||||
If there are any problems or suggestions, please feel free to ask on the [discord server](https://discord.com/invite/u9yhsFmEkB) or [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
@ -106,4 +130,11 @@ If you have any code contributions in mind, please feel free to say Hi to us on
|
||||
# Disclaimer
|
||||
The authors of this project are not responsible for any content generated using this interface.
|
||||
|
||||
The 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, spread misinformation, or target vulnerable groups. For the full list of restrictions please read [the license](LICENSE). You agree to these terms by using this software.
|
||||
The license of this software forbids you from sharing any content that:
|
||||
- Violates any laws.
|
||||
- Produces any harm to a person or persons.
|
||||
- Disseminates (spreads) any personal information that would be meant for harm.
|
||||
- Spreads misinformation.
|
||||
- Target vulnerable groups.
|
||||
|
||||
For the full list of restrictions please read [the License](LICENSE). You agree to these terms by using this software.
|
||||
|
@ -1 +0,0 @@
|
||||
Moved to https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting
|
@ -25,7 +25,7 @@
|
||||
<div id="logo">
|
||||
<h1>
|
||||
Stable Diffusion UI
|
||||
<small>v2.4.19 <span id="updateBranchLabel"></span></small>
|
||||
<small>v2.4.21 <span id="updateBranchLabel"></span></small>
|
||||
</h1>
|
||||
</div>
|
||||
<div id="server-status">
|
||||
@ -100,7 +100,11 @@
|
||||
</div>
|
||||
|
||||
<button id="makeImage" class="primaryButton">Make Image</button>
|
||||
<div id="render-buttons">
|
||||
<button id="stopImage" class="secondaryButton">Stop All</button>
|
||||
<button id="pause"><i class="fa-solid fa-pause"></i> Pause All</button>
|
||||
<button id="resume"><i class="fa-solid fa-play"></i> Resume</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="line-separator"></span>
|
||||
|
@ -136,6 +136,10 @@
|
||||
background: var(--background-color3);
|
||||
}
|
||||
|
||||
.editor-controls-right .image-editor-button {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
#init_image_button_inpaint .input-toggle {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
|
@ -191,15 +191,29 @@ code {
|
||||
background: rgb(132, 8, 0);
|
||||
border: 2px solid rgb(122, 29, 0);
|
||||
color: rgb(255, 221, 255);
|
||||
width: 100%;
|
||||
height: 30pt;
|
||||
border-radius: 6px;
|
||||
display: none;
|
||||
margin-top: 2pt;
|
||||
flex-grow: 2;
|
||||
}
|
||||
#stopImage:hover {
|
||||
background: rgb(177, 27, 0);
|
||||
}
|
||||
|
||||
div#render-buttons {
|
||||
gap: 3px;
|
||||
margin-top: 4px;
|
||||
display: none;
|
||||
}
|
||||
button#pause {
|
||||
flex-grow: 1;
|
||||
background: var(--accent-color);
|
||||
}
|
||||
button#resume {
|
||||
flex-grow: 1;
|
||||
background: var(--accent-color);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
@ -265,7 +279,7 @@ img {
|
||||
}
|
||||
.preview-prompt {
|
||||
font-size: 13pt;
|
||||
margin-bottom: 10pt;
|
||||
display: inline;
|
||||
}
|
||||
#coffeeButton {
|
||||
height: 23px;
|
||||
@ -391,14 +405,34 @@ img {
|
||||
.imageTaskContainer > div > .collapsible-handle {
|
||||
display: none;
|
||||
}
|
||||
.dropTargetBefore::before{
|
||||
content: "";
|
||||
border: 1px solid #fff;
|
||||
margin-bottom: -2px;
|
||||
display: block;
|
||||
box-shadow: 0 0 5px #fff;
|
||||
transform: translate(0px, -14px);
|
||||
}
|
||||
.dropTargetAfter::after{
|
||||
content: "";
|
||||
border: 1px solid #fff;
|
||||
margin-bottom: -2px;
|
||||
display: block;
|
||||
box-shadow: 0 0 5px #fff;
|
||||
transform: translate(0px, 14px);
|
||||
}
|
||||
.drag-handle {
|
||||
margin-right: 6px;
|
||||
cursor: move;
|
||||
}
|
||||
.taskStatusLabel {
|
||||
float: left;
|
||||
font-size: 8pt;
|
||||
background:var(--background-color2);
|
||||
border: 1px solid rgb(61, 62, 66);
|
||||
padding: 2pt 4pt;
|
||||
border-radius: 2pt;
|
||||
margin-right: 5pt;
|
||||
display: inline;
|
||||
}
|
||||
.activeTaskLabel {
|
||||
background:rgb(0, 90, 30);
|
||||
@ -448,6 +482,7 @@ img {
|
||||
font-size: 10pt;
|
||||
color: #aaa;
|
||||
margin-bottom: 5pt;
|
||||
margin-top: 5pt;
|
||||
}
|
||||
.img-batch {
|
||||
display: inline;
|
||||
@ -1068,7 +1103,19 @@ div.task-initimg > img {
|
||||
}
|
||||
div.task-fs-initimage {
|
||||
display: none;
|
||||
# position: absolute;
|
||||
}
|
||||
div.task-initimg:hover div.task-fs-initimage {
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
box-shadow: 0 0 30px #000;
|
||||
margin-top:-64px;
|
||||
}
|
||||
div.top-right {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
button#save-system-settings-btn {
|
||||
@ -1080,3 +1127,50 @@ button#save-system-settings-btn {
|
||||
#ip-info div {
|
||||
line-height: 200%;
|
||||
}
|
||||
|
||||
/* SCROLLBARS */
|
||||
:root {
|
||||
--scrollbar-width: 14px;
|
||||
--scrollbar-radius: 10px;
|
||||
}
|
||||
|
||||
.scrollbar-editor::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.scrollbar-editor::-webkit-scrollbar-track {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.scrollbar-editor::-webkit-scrollbar-thumb {
|
||||
background: --background-color2;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: var(--scrollbar-width);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 5px var(--input-border-color);
|
||||
border-radius: var(--input-border-radius);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--background-color2);
|
||||
border-radius: var(--scrollbar-radius);
|
||||
}
|
||||
|
||||
body.pause {
|
||||
border: solid 12px var(--accent-color);
|
||||
}
|
||||
|
||||
body.wait-pause {
|
||||
animation: blinker 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
0% { border: solid 12px var(--accent-color); }
|
||||
50% { border: solid 12px var(--background-color1); }
|
||||
100% { border: solid 12px var(--accent-color); }
|
||||
}
|
||||
|
@ -8,10 +8,10 @@
|
||||
const SERVER_STATE_VALIDITY_DURATION = 90 * 1000 // ms - 90 seconds to allow ping to timeout more than once before killing tasks.
|
||||
const HEALTH_PING_INTERVAL = 5000 // ms
|
||||
const IDLE_COOLDOWN = 2500 // ms
|
||||
const CONCURRENT_TASK_INTERVAL = 500 // ms
|
||||
const CONCURRENT_TASK_INTERVAL = 100 // ms
|
||||
|
||||
/** Connects to an endpoint and resumes connexion after reaching end of stream until all data is received.
|
||||
* Allows closing the connexion while the server buffers more data.
|
||||
/** Connects to an endpoint and resumes connection after reaching end of stream until all data is received.
|
||||
* Allows closing the connection while the server buffers more data.
|
||||
*/
|
||||
class ChunkedStreamReader {
|
||||
#bufferedString = '' // Data received waiting to be read.
|
||||
@ -264,11 +264,11 @@
|
||||
|
||||
function isServerAvailable() {
|
||||
if (typeof serverState !== 'object') {
|
||||
console.error('serverState not set to a value. Connexion to server could be lost...')
|
||||
console.error('serverState not set to a value. Connection to server could be lost...')
|
||||
return false
|
||||
}
|
||||
if (Date.now() >= serverState.time + SERVER_STATE_VALIDITY_DURATION) {
|
||||
console.warn('SERVER_STATE_VALIDITY_DURATION elapsed. Connexion to server could be lost...')
|
||||
console.warn('SERVER_STATE_VALIDITY_DURATION elapsed. Connection to server could be lost...')
|
||||
return false
|
||||
}
|
||||
switch (serverState.status) {
|
||||
@ -306,7 +306,7 @@
|
||||
if (await healthCheck() && isServerAvailable()) { // Force a recheck of server status before failure...
|
||||
continue // Continue waiting if last healthCheck confirmed the server is still alive.
|
||||
}
|
||||
throw new Error('Connexion with server lost.')
|
||||
throw new Error('Connection with server lost.')
|
||||
}
|
||||
}
|
||||
if (Date.now() >= serverState.time + SERVER_STATE_VALIDITY_DURATION) {
|
||||
@ -837,6 +837,8 @@
|
||||
* @memberof Task
|
||||
*/
|
||||
async post(timeout=-1) {
|
||||
performance.mark('make-render-request')
|
||||
console.log('delay between clicking and making the server request:', performance.measure('diff', 'click-makeImage', 'make-render-request').duration + ' ms')
|
||||
let jsonResponse = await super.post('/render', timeout)
|
||||
if (typeof jsonResponse?.task !== 'number') {
|
||||
console.warn('Endpoint error response: ', jsonResponse)
|
||||
@ -1104,9 +1106,9 @@
|
||||
idleEventPromise = makeQuerablePromise(eventSource.fireEvent(EVENT_IDLE, {capacity: serverCapacity, idle: true}))
|
||||
}
|
||||
// Calling idle could result in task being added to queue.
|
||||
if (task_queue.size <= 0 && concurrent_generators.size <= 0) {
|
||||
return asyncDelay(IDLE_COOLDOWN).then(() => idleEventPromise)
|
||||
}
|
||||
// if (task_queue.size <= 0 && concurrent_generators.size <= 0) {
|
||||
// return asyncDelay(IDLE_COOLDOWN).then(() => idleEventPromise)
|
||||
// }
|
||||
}
|
||||
if (task_queue.size < serverCapacity) {
|
||||
if (!idleEventPromise?.isPending) {
|
||||
|
@ -105,7 +105,26 @@ const IMAGE_EDITOR_ACTIONS = [
|
||||
icon: "fa-solid fa-xmark",
|
||||
handler: (editor) => {
|
||||
editor.ctx_current.clearRect(0, 0, editor.width, editor.height)
|
||||
}
|
||||
},
|
||||
trackHistory: true
|
||||
},
|
||||
{
|
||||
id: "undo",
|
||||
name: "Undo",
|
||||
icon: "fa-solid fa-rotate-left",
|
||||
handler: (editor) => {
|
||||
editor.history.undo()
|
||||
},
|
||||
trackHistory: false
|
||||
},
|
||||
{
|
||||
id: "redo",
|
||||
name: "Redo",
|
||||
icon: "fa-solid fa-rotate-right",
|
||||
handler: (editor) => {
|
||||
editor.history.redo()
|
||||
},
|
||||
trackHistory: false
|
||||
}
|
||||
]
|
||||
|
||||
@ -436,13 +455,14 @@ class ImageEditor {
|
||||
return
|
||||
}
|
||||
|
||||
var max_size = Math.min(parseInt(window.innerWidth * 0.9), width, 768)
|
||||
if (width > height) {
|
||||
var max_size = Math.min(parseInt(window.innerWidth * 0.9), width, 768)
|
||||
var multiplier = max_size / width
|
||||
width = (multiplier * width).toFixed()
|
||||
height = (multiplier * height).toFixed()
|
||||
}
|
||||
else {
|
||||
var max_size = Math.min(parseInt(window.innerHeight * 0.9), height, 768)
|
||||
var multiplier = max_size / height
|
||||
width = (multiplier * width).toFixed()
|
||||
height = (multiplier * height).toFixed()
|
||||
@ -525,7 +545,9 @@ class ImageEditor {
|
||||
}
|
||||
runAction(action_id) {
|
||||
var action = IMAGE_EDITOR_ACTIONS.find(a => a.id == action_id)
|
||||
if (action.trackHistory) {
|
||||
this.history.pushAction(action_id)
|
||||
}
|
||||
action.handler(this)
|
||||
}
|
||||
setBrush(layer = null, options = null) {
|
||||
|
@ -47,6 +47,9 @@ let streamImageProgressField = document.querySelector("#stream_image_progress")
|
||||
|
||||
let makeImageBtn = document.querySelector('#makeImage')
|
||||
let stopImageBtn = document.querySelector('#stopImage')
|
||||
let pauseBtn = document.querySelector('#pause')
|
||||
let resumeBtn = document.querySelector('#resume')
|
||||
let renderButtons = document.querySelector('#render-buttons')
|
||||
|
||||
let imagesContainer = document.querySelector('#current-images')
|
||||
let initImagePreviewContainer = document.querySelector('#init_image_preview_container')
|
||||
@ -103,6 +106,8 @@ imagePreview.addEventListener('drop', function(ev) {
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
let showConfigToggle = document.querySelector('#configToggleBtn')
|
||||
// let configBox = document.querySelector('#config')
|
||||
// let outputMsg = document.querySelector('#outputMsg')
|
||||
@ -426,6 +431,7 @@ function getUncompletedTaskEntries() {
|
||||
}
|
||||
|
||||
function makeImage() {
|
||||
performance.mark('click-makeImage')
|
||||
if (!SD.isServerAvailable()) {
|
||||
alert('The server is not available.')
|
||||
return
|
||||
@ -458,6 +464,10 @@ function makeImage() {
|
||||
|
||||
async function onIdle() {
|
||||
const serverCapacity = SD.serverCapacity
|
||||
if (pauseClient===true) {
|
||||
await resumeClient()
|
||||
}
|
||||
|
||||
for (const taskEntry of getUncompletedTaskEntries()) {
|
||||
if (SD.activeTasks.size >= serverCapacity) {
|
||||
break
|
||||
@ -625,19 +635,18 @@ function onTaskCompleted(task, reqBody, instance, outputContainer, stepUpdate) {
|
||||
task['stopTask'].innerHTML = '<i class="fa-solid fa-trash-can"></i> Remove'
|
||||
task['taskStatusLabel'].style.display = 'none'
|
||||
|
||||
let time = Date.now() - task.startTime
|
||||
time /= 1000
|
||||
let time = millisecondsToStr( Date.now() - task.startTime )
|
||||
|
||||
if (task.batchesDone == task.batchCount) {
|
||||
if (!task.outputMsg.innerText.toLowerCase().includes('error')) {
|
||||
task.outputMsg.innerText = `Processed ${task.numOutputsTotal} images in ${time} seconds`
|
||||
task.outputMsg.innerText = `Processed ${task.numOutputsTotal} images in ${time}`
|
||||
}
|
||||
task.progressBar.style.height = "0px"
|
||||
task.progressBar.style.border = "0px solid var(--background-color3)"
|
||||
task.progressBar.classList.remove("active")
|
||||
setStatus('request', 'done', 'success')
|
||||
} else {
|
||||
task.outputMsg.innerText += `Task ended after ${time} seconds`
|
||||
task.outputMsg.innerText += `Task ended after ${time}`
|
||||
}
|
||||
|
||||
if (randomSeedField.checked) {
|
||||
@ -652,7 +661,7 @@ function onTaskCompleted(task, reqBody, instance, outputContainer, stepUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
stopImageBtn.style.display = 'none'
|
||||
renderButtons.style.display = 'none'
|
||||
renameMakeImageButton()
|
||||
|
||||
if (isSoundEnabled()) {
|
||||
@ -737,7 +746,7 @@ async function onTaskStart(task) {
|
||||
)
|
||||
|
||||
setStatus('request', 'fetching..')
|
||||
stopImageBtn.style.display = 'block'
|
||||
renderButtons.style.display = 'flex'
|
||||
renameMakeImageButton()
|
||||
previewTools.style.display = 'block'
|
||||
}
|
||||
@ -745,23 +754,33 @@ async function onTaskStart(task) {
|
||||
/* Hover effect for the init image in the task list */
|
||||
function createInitImageHover(taskEntry) {
|
||||
var $tooltip = $( taskEntry.querySelector('.task-fs-initimage') )
|
||||
$( taskEntry.querySelector('div.task-initimg > img') ).on('mouseenter', function() {
|
||||
var img = this,
|
||||
$img = $(img),
|
||||
offset = $img.offset();
|
||||
var img = document.createElement('img')
|
||||
img.src = taskEntry.querySelector('div.task-initimg > img').src
|
||||
$tooltip.append(img)
|
||||
$tooltip.append(`<div class="top-right"><button>Use as Input</button></div>`)
|
||||
$tooltip.find('button').on('click', (e) => { onUseAsInputClick(null,img) } )
|
||||
}
|
||||
|
||||
$tooltip
|
||||
.css({
|
||||
'top': offset.top,
|
||||
'left': offset.left,
|
||||
'z-index': 99999,
|
||||
'display': 'block'
|
||||
})
|
||||
.append($img.clone().css({width:"", height:""}));
|
||||
})
|
||||
$tooltip.on('mouseleave', function() {
|
||||
$tooltip.empty().addClass('hidden');
|
||||
let startX, startY;
|
||||
function onTaskEntryDragOver(event) {
|
||||
imagePreview.querySelectorAll(".imageTaskContainer").forEach(itc => {
|
||||
if(itc != event.target.closest(".imageTaskContainer")){
|
||||
itc.classList.remove('dropTargetBefore','dropTargetAfter');
|
||||
}
|
||||
});
|
||||
if(event.target.closest(".imageTaskContainer")){
|
||||
if(startX && startY){
|
||||
if(event.target.closest(".imageTaskContainer").offsetTop > startY){
|
||||
event.target.closest(".imageTaskContainer").classList.add('dropTargetAfter');
|
||||
}else if(event.target.closest(".imageTaskContainer").offsetTop < startY){
|
||||
event.target.closest(".imageTaskContainer").classList.add('dropTargetBefore');
|
||||
}else if (event.target.closest(".imageTaskContainer").offsetLeft > startX){
|
||||
event.target.closest(".imageTaskContainer").classList.add('dropTargetAfter');
|
||||
}else if (event.target.closest(".imageTaskContainer").offsetLeft < startX){
|
||||
event.target.closest(".imageTaskContainer").classList.add('dropTargetBefore');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createTask(task) {
|
||||
@ -801,10 +820,11 @@ function createTask(task) {
|
||||
taskEntry.id = `imageTaskContainer-${Date.now()}`
|
||||
taskEntry.className = 'imageTaskContainer'
|
||||
taskEntry.innerHTML = ` <div class="header-content panel collapsible active">
|
||||
<i class="drag-handle fa-solid fa-grip"></i>
|
||||
<div class="taskStatusLabel">Enqueued</div>
|
||||
<button class="secondaryButton stopTask"><i class="fa-solid fa-trash-can"></i> Remove</button>
|
||||
<button class="secondaryButton useSettings"><i class="fa-solid fa-redo"></i> Use these settings</button>
|
||||
<div class="preview-prompt collapsible active"></div>
|
||||
<div class="preview-prompt"></div>
|
||||
<div class="taskConfig">${taskConfig}</div>
|
||||
<div class="outputMsg"></div>
|
||||
<div class="progress-bar active"><div></div></div>
|
||||
@ -815,6 +835,23 @@ function createTask(task) {
|
||||
|
||||
createCollapsibles(taskEntry)
|
||||
|
||||
let draghandle = taskEntry.querySelector('.drag-handle')
|
||||
draghandle.addEventListener('mousedown', (e) => { taskEntry.setAttribute('draggable',true)})
|
||||
draghandle.addEventListener('mouseup', (e) => { taskEntry.setAttribute('draggable',false)})
|
||||
taskEntry.addEventListener('dragend', (e) => {
|
||||
taskEntry.setAttribute('draggable',false);
|
||||
imagePreview.querySelectorAll(".imageTaskContainer").forEach(itc => {
|
||||
itc.classList.remove('dropTargetBefore','dropTargetAfter');
|
||||
});
|
||||
imagePreview.removeEventListener("dragover", onTaskEntryDragOver );
|
||||
})
|
||||
taskEntry.addEventListener('dragstart', function(e) {
|
||||
imagePreview.addEventListener("dragover", onTaskEntryDragOver );
|
||||
e.dataTransfer.setData("text/plain", taskEntry.id);
|
||||
startX = e.target.closest(".imageTaskContainer").offsetLeft;
|
||||
startY = e.target.closest(".imageTaskContainer").offsetTop;
|
||||
})
|
||||
|
||||
|
||||
if (task.reqBody.init_image !== undefined) {
|
||||
createInitImageHover(taskEntry)
|
||||
@ -847,24 +884,13 @@ function createTask(task) {
|
||||
|
||||
task.isProcessing = true
|
||||
taskEntry = imagePreview.insertBefore(taskEntry, previewTools.nextSibling)
|
||||
taskEntry.draggable = true
|
||||
htmlTaskMap.set(taskEntry, task)
|
||||
taskEntry.addEventListener('dragstart', function(ev) {
|
||||
ev.dataTransfer.setData("text/plain", ev.target.id);
|
||||
})
|
||||
|
||||
task.previewPrompt.innerText = task.reqBody.prompt
|
||||
if (task.previewPrompt.innerText.trim() === '') {
|
||||
task.previewPrompt.innerHTML = ' ' // allows the results to be collapsed
|
||||
}
|
||||
|
||||
// Allow prompt text to be selected.
|
||||
task.previewPrompt.addEventListener("mouseover", function() {
|
||||
taskEntry.setAttribute("draggable", "false");
|
||||
});
|
||||
task.previewPrompt.addEventListener("mouseout", function() {
|
||||
taskEntry.setAttribute("draggable", "true");
|
||||
});
|
||||
}
|
||||
|
||||
function getCurrentUserRequest() {
|
||||
@ -936,21 +962,30 @@ function getPrompts(prompts) {
|
||||
if (typeof prompts === 'undefined') {
|
||||
prompts = promptField.value
|
||||
}
|
||||
if (prompts.trim() === '') {
|
||||
if (prompts.trim() === '' && activeTags.length === 0) {
|
||||
return ['']
|
||||
}
|
||||
|
||||
let promptsToMake = []
|
||||
if (prompts.trim() !== '') {
|
||||
prompts = prompts.split('\n')
|
||||
prompts = prompts.map(prompt => prompt.trim())
|
||||
prompts = prompts.filter(prompt => prompt !== '')
|
||||
|
||||
let promptsToMake = applyPermuteOperator(prompts)
|
||||
promptsToMake = applyPermuteOperator(prompts)
|
||||
promptsToMake = applySetOperator(promptsToMake)
|
||||
}
|
||||
const newTags = activeTags.filter(tag => tag.inactive === undefined || tag.inactive === false)
|
||||
if (newTags.length > 0) {
|
||||
const promptTags = newTags.map(x => x.name).join(", ")
|
||||
if (promptsToMake.length > 0) {
|
||||
promptsToMake = promptsToMake.map((prompt) => `${prompt}, ${promptTags}`)
|
||||
}
|
||||
else
|
||||
{
|
||||
promptsToMake.push(promptTags)
|
||||
}
|
||||
}
|
||||
|
||||
promptsToMake = applyPermuteOperator(promptsToMake)
|
||||
promptsToMake = applySetOperator(promptsToMake)
|
||||
@ -1389,6 +1424,7 @@ function selectTab(tab_id) {
|
||||
tabInfo.tab.classList.toggle("active")
|
||||
tabInfo.content.classList.toggle("active")
|
||||
}
|
||||
document.dispatchEvent(new CustomEvent('tabClick', { detail: tabInfo }))
|
||||
}
|
||||
function linkTabContents(tab) {
|
||||
var name = tab.id.replace("tab-", "")
|
||||
@ -1402,6 +1438,37 @@ function linkTabContents(tab) {
|
||||
tab.addEventListener("click", event => selectTab(tab.id))
|
||||
}
|
||||
|
||||
let pauseClient = false
|
||||
|
||||
function resumeClient() {
|
||||
if (pauseClient) {
|
||||
document.body.classList.remove('wait-pause')
|
||||
document.body.classList.add('pause')
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
let playbuttonclick = function () {
|
||||
resumeBtn.removeEventListener("click", playbuttonclick);
|
||||
resolve("resolved");
|
||||
}
|
||||
resumeBtn.addEventListener("click", playbuttonclick)
|
||||
})
|
||||
}
|
||||
|
||||
pauseBtn.addEventListener("click", function () {
|
||||
pauseClient = true
|
||||
pauseBtn.style.display="none"
|
||||
resumeBtn.style.display = "inline"
|
||||
document.body.classList.add('wait-pause')
|
||||
})
|
||||
|
||||
resumeBtn.addEventListener("click", function () {
|
||||
pauseClient = false
|
||||
resumeBtn.style.display = "none"
|
||||
pauseBtn.style.display = "inline"
|
||||
document.body.classList.remove('pause')
|
||||
document.body.classList.remove('wait-pause')
|
||||
})
|
||||
|
||||
document.querySelectorAll(".tab").forEach(linkTabContents)
|
||||
|
||||
window.addEventListener("beforeunload", function(e) {
|
||||
|
@ -45,6 +45,7 @@ function toggleCollapsible(element) {
|
||||
handle.innerHTML = '➖' // minus
|
||||
}
|
||||
}
|
||||
document.dispatchEvent(new CustomEvent('collapsibleClick', { detail: collapsibleHeader }))
|
||||
|
||||
if (COLLAPSIBLES_INITIALIZED && COLLAPSIBLE_PANELS.includes(element)) {
|
||||
saveCollapsibles()
|
||||
|
@ -43,22 +43,22 @@
|
||||
</style>
|
||||
`)
|
||||
|
||||
const markedScript = document.createElement('script')
|
||||
markedScript.src = '/media/js/marked.min.js'
|
||||
|
||||
markedScript.onload = async function() {
|
||||
loadScript('/media/js/marked.min.js').then(async function() {
|
||||
let appConfig = await fetch('/get/app_config')
|
||||
if (!appConfig.ok) {
|
||||
console.error('[release-notes] Failed to get app_config.')
|
||||
return
|
||||
}
|
||||
appConfig = await appConfig.json()
|
||||
|
||||
let updateBranch = appConfig.update_branch || 'main'
|
||||
const updateBranch = appConfig.update_branch || 'main'
|
||||
|
||||
let releaseNotes = await fetch(`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/${updateBranch}/CHANGES.md`)
|
||||
if (releaseNotes.status != 200) {
|
||||
if (!releaseNotes.ok) {
|
||||
console.error('[release-notes] Failed to get CHANGES.md.')
|
||||
return
|
||||
}
|
||||
releaseNotes = await releaseNotes.text()
|
||||
news.innerHTML = marked.parse(releaseNotes)
|
||||
}
|
||||
|
||||
document.querySelector('body').appendChild(markedScript)
|
||||
})
|
||||
})()
|
Loading…
Reference in New Issue
Block a user