stable-diffusion-for-fun/templates/index.html

1351 lines
70 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<html>
<head>
<meta charset="utf-8">
<title>{{ config.TITLE }}</title>
<meta name="description" content="Stable Diffusion Online">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link href="{{ url_for('static',filename='bootstrap.min.css') }}" rel="stylesheet">
</head>
<body>
<div class="container">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="#">{{ config.TITLE }}</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" data-en_XX="Home" data-zh_CN="主页" href=".">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" data-en_XX="Restoration" data-zh_CN="图像修复"
href="restoration">Restoration</a>
</li>
<li class="nav-item">
<a class="nav-link" data-en_XX="Help" data-zh_CN="帮助">Help</a>
</li>
<li class="nav-item">
<a class="nav-link" data-en_XX="About" data-zh_CN="关于">About</a>
</li>
</ul>
</div>
<div class="col-md-4">
<div class="input-group">
<label class="input-group-text" for="language" data-en_XX="Language"
data-zh_CN="语言">Language</label>
<select class="form-select" id="language">
<option value="zh_CN">中文(测试)</option>
<option selected value="en_XX">English</option>
</select>
</div>
</div>
</div>
</nav>
<div class="card mb-3">
<div class="card-header">
<span>Stable Diffusion</span>
</div>
<div class="card-body">
<div class="row">
<div class="col mb-3">
<div class="input-group">
<label for="apiKey" class="input-group-text" data-en_XX="API Key" data-zh_CN="API 密钥">API
Key</label>
<input type="password" class="form-control" id="apiKey" value="">
<div class="input-group-text">
<input class="form-check-input" type="checkbox" value="" id="isPrivate">
</div>
<label for="isPrivate" class="input-group-text" data-en_XX="Generate Private Images"
data-zh_CN="生成非公开图片">Generate Private Images</label>
</div>
</div>
</div>
<div class="row mb-3">
<div class="input-group">
<label for="prompt" class="input-group-text" data-en_XX="This image is.."
data-zh_CN="这张图..">This image is..</label>
<input class="form-control" id="prompt" aria-describedby="promptHelp">
</div>
<div id="promptHelp" class="form-text" data-en_XX="Describe your image. Example: photo of a cat, cute, black and white. Use () to
emphasize." data-zh_CN="形容这张图。比如:一张猫的照片,可爱,黑白色。用括号()强调重要性。">Describe your image. Example: photo
of a cat,
cute, black and white. Use () to emphasize.</div>
</div>
<div class="row mb-3">
<div class="input-group">
<label for="negPrompt" class="input-group-text" data-en_XX="This image is NOT.."
data-zh_CN="这张图不是..">This image is NOT..</label>
<input type="text" class="form-control" id="negPrompt" aria-describedby="negPromptHelp">
</div>
<div id="negPromptHelp" class="form-text" data-en_XX="Optional. Describe what's NOT the image."
data-zh_CN="非必填。形容这张图不长什么样。">Optional. Describe what's NOT the image.</div>
</div>
<div class="row">
<div class="col-md-2 mb-3">
<div class="input-group input-group-sm">
<label for="inputSeed" class="input-group-text" data-en_XX="Seed"
data-zh_CN="随机数">Seed</label>
<input type="text" class="form-control" id="inputSeed" aria-describedby="inputSeedHelp">
</div>
<div id="inputSeedHelp" class="form-text"
data-en_XX="Leave it empty or set 0 to use a random seed" data-zh_CN="非必填。留白或填0使用默认随机数">
Leave it empty or set 0 to use a random seed
</div>
</div>
<div class="col-md-2 mb-3">
<div class="input-group input-group-sm">
<label for="guidanceScale" class="input-group-text" data-en_XX="Guidance"
data-zh_CN="指示强度">Guidance</label>
<input type="number" class="form-control" id="inputGuidanceScale"
aria-describedby="inputGuidanceScaleHelp" placeholder="7.5" min="1" max="30">
</div>
<div id="inputGuidanceScaleHelp" class="form-text"
data-en_XX="20 = follow prompt, 7 = creative/artistic. Lower it if you see bad images."
data-zh_CN="20 = 提示词重要7 = 更有创造性。如果结果较差,适当降低该数。">
20 = follow prompt, 7 = creative/artistic. Lower it if you see bad images.
</div>
</div>
<div class="col-md-2 mb-3">
<div class="input-group input-group-sm">
<label for="strength" class="input-group-text" data-en_XX="Strength"
data-zh_CN="改变程度">Strength</label>
<input type="number" class="form-control" id="inputStrength"
aria-describedby="inputStrengthHelp" placeholder="0.5" min="0" max="1">
</div>
<div id="inputStrengthHelp" class="form-text"
data-en_XX="How different from the original image. 0 means the same, 1 means very different. Not used in text only or inpainting image."
data-zh_CN="和原图有多么的不同。0指一样1指非常不一样。不适用于【文字->图片】或【图片重建】">How different from the
original image. 0 means the same, 1 means very different. Not used in text only or
inpainting image.
</div>
</div>
<div class="col-md-2 mb-3">
<div class="input-group input-group-sm">
<label for="inputSteps" class="input-group-text" data-en_XX="Steps"
data-zh_CN="迭代次数">Steps</label>
<input type="number" class="form-control" id="inputSteps" aria-describedby="inputStepsHelp"
placeholder="50">
</div>
<div id="inputStepsHelp" class="form-text"
data-en_XX="More steps better image but longer time to generate"
data-zh_CN="迭代次数越多图片越好,但生成时间越久">More steps better image but longer time to generate
</div>
</div>
<div class="col-md-2 mb-3">
<div class="input-group input-group-sm">
<label for="inputWidth" class="input-group-text" data-en_XX="Width"
data-zh_CN="图片宽度">Width</label>
<input type="number" class="form-control" id="inputWidth" placeholder="512" min="1"
max="1024">
</div>
</div>
<div class="col-md-2 mb-3">
<div class="input-group input-group-sm">
<label for="inputHeight" class="input-group-text" data-en_XX="Height"
data-zh_CN="图片高度">Height</label>
<input type="number" class="form-control" id="inputHeight" placeholder="512" min="1"
max="1024">
</div>
</div>
</div>
<div class="card">
<div class="stable-diffusion card-header">
<ul class="nav nav-pills card-header-pills">
<li class="nav-item">
<a class="nav-link" href="#card-txt" data-en_XX="Text-to-Image"
data-zh_CN="文字->图片">Text-to-Image</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#card-img" data-en_XX="Image-to-Image"
data-zh_CN="图片->图片">Image-to-Image</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#card-drawing" data-en_XX="Drawing"
data-zh_CN="绘画创作">Drawing</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#card-inpainting" data-en_XX="Inpainting"
data-zh_CN="图像重建">Inpainting</a>
</li>
</ul>
</div>
<div class="card-body stable-diffusion-specific" id="card-img" style="display:none">
<div class="row">
<div class="col-md-4">
<div class="card">
<div class="card-header" data-en_XX="1. Choose Reference Image"
data-zh_CN="1. 选择参照图">
1. Choose Reference Image
</div>
<div class="card-body">
<div class="row">
<button id="copy-txt-to-img" class="btn btn-primary mb-3"
data-en_XX="Copy from text-to-image" data-zh_CN="从【文字->图片】结果复制">Copy
from text-to-image</button>
<button id="copy-last-img" class="btn btn-primary mb-3"
data-en_XX="Copy from last image result" data-zh_CN="从【图片->图片】结果复制">Copy
from last image result</button>
<button id="upload-img" class="btn btn-primary mb-3"
data-en_XX="Upload image" data-zh_CN="上传一张图片">Upload
image</button>
</div>
<div class="row">
<button id="newImg2ImgJob" class="btn btn-primary mb-3"
data-en_XX="Let's Go with Image Below!" data-zh_CN="就用下面的图生成!">Let's
Go with Image Below!</button>
</div>
</div>
<img class="card-img-bottom" id="reference-img">
</div>
</div>
<div class="col-md-8">
<div class="card">
<div class="card-header" data-en_XX="2. Result" data-zh_CN="2. 结果">
2. Result
</div>
<div class="card-body">
<ul class="list-group">
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="img2ImgJobUUID"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job UUID"
data-zh_CN="图片唯一识别码">Job UUID</span>
</li>
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="img2ImgStatus"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job Status"
data-zh_CN="生成状态">Job Status</span>
</li>
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="img2ImgSeed"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job Seed"
data-zh_CN="图片随机数">Job Seed</span>
</li>
</ul>
</div>
<img class="card-img-bottom downloadable-img" id="img2ImgImg">
</div>
</div>
</div>
</div>
<div class="card-body stable-diffusion-specific" id="card-drawing" style="display:none">
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header" data-en_XX="1. Draw It" data-zh_CN="1. 画画">
1. Draw It
</div>
<div class="card-body">
<div class="row" data-en_XX="Recommend to draw on a bigger screen below"
data-zh_CN="推荐用大屏幕在下面作画">Recommend to draw on a bigger screen below</div>
<div class="row">
<button id="newDrawingJob" class="btn btn-primary mb-3"
data-en_XX="Let's Go with Sketching Below!"
data-zh_CN="就用下面的草稿生成!">Let's
Let's Go with Sketching Below!</button>
</div>
<div class="input-group mb-3">
<button id="drawing-reset" class="btn btn-outline-secondary"
data-en_XX="Reset" data-zh_CN="重置">Reset</button>
<button id="drawing-undo" class="btn btn-outline-secondary"
data-en_XX="Undo" data-zh_CN="撤销">Undo</button>
<button id="drawing-redo" class="btn btn-outline-secondary"
data-en_XX="Redo" data-zh_CN="重做">Redo</button>
</div>
<div class="form-row">
<label for="drawing-strike-size" class="form-label" data-en_XX="Strike Size"
data-zh_CN="笔触大小">Strike Size</label>
<input type="range" class="form-range" min="1" max="50"
id="drawing-strike-size">
<output id='drawing-range-value'></output>
</div>
</div>
<div class="card-img-bottom" id="drawing-mask-canvas-container"
style="cursor: pointer; border: 1px black solid;">
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header" data-en_XX="2. Result" data-zh_CN="2. 结果">
2. Result
</div>
<div class="card-body">
<ul class="list-group">
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="drawingJobUUID"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job UUID"
data-zh_CN="图片唯一识别码">Job UUID</span>
</li>
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="drawingStatus"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job Status"
data-zh_CN="生成状态">Job Status</span>
</li>
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="drawingSeed"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job Seed"
data-zh_CN="图片随机数">Job Seed</span>
</li>
</ul>
</div>
<img class="card-img-bottom downloadable-img" id="drawingImg">
</div>
</div>
</div>
</div>
<div class="card-body stable-diffusion-specific" id="card-inpainting" style="display:none">
<div class="row">
<div class="col-md-4">
<div class="card">
<div class="card-header" data-en_XX="1. Choose Original Image" data-zh_CN="1. 选择原图">
1. Choose Original Image
</div>
<div class="card-body">
<div class="row">
<button id="copy-txt-to-img-inpaint" class="btn btn-primary mb-3"
data-en_XX="Copy from text-to-image" data-zh_CN="从【文字->图片】结果复制">Copy
from text-to-image</button>
<button id="copy-last-img-inpaint" class="btn btn-primary mb-3"
data-en_XX="Copy from last image result" data-zh_CN="从【图片->图片】结果复制">Copy
from last image result</button>
<button id="upload-img-inpaint" class="btn btn-primary mb-3"
data-en_XX="Upload image" data-zh_CN="上传一张图片">Upload
image</button>
</div>
</div>
<img class="card-img-bottom" id="inpaint-img">
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header" data-en_XX="2. Remove Area (Mask)"
data-zh_CN="2. 抹去要修复的部分">
2. Remove Area (Mask)
</div>
<div class="card-body">
<div class="row">
<button id="newInpaintingJob" class="btn btn-primary mb-3"
data-en_XX="Let's Go with Image + Mask Below!" data-zh_CN="进行修复!">Let's
Go with Image + Mask Below!</button>
</div>
<div class="input-group mb-3">
<button id="inpaint-reset" class="btn btn-outline-secondary"
data-en_XX="Reset" data-zh_CN="重置">Reset</button>
<button id="inpaint-undo" class="btn btn-outline-secondary"
data-en_XX="Undo" data-zh_CN="撤销">Undo</button>
<button id="inpaint-redo" class="btn btn-outline-secondary"
data-en_XX="Redo" data-zh_CN="重做">Redo</button>
</div>
<div class="form-row">
<label for="inpaint-strike-size" class="form-label" data-en_XX="Strike Size"
data-zh_CN="笔触大小">Strike Size</label>
<input type="range" class="form-range" min="1" max="50"
id="inpaint-strike-size">
<output id='inpaint-range-value'></output>
</div>
</div>
<div class="card-img-bottom" style="position: relative;">
<img id="inpaint-img-for-mask" width="100%">
<div id="inpaint-mask-canvas-container"
style="cursor: pointer; position: absolute; top: 0; left: 0;">
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header" data-en_XX="3. Result" data-zh_CN="3. 结果">
3. Result
</div>
<div class="card-body">
<ul class="list-group">
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="inpaintJobUUID"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job UUID"
data-zh_CN="图片唯一识别码">Job UUID</span>
</li>
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="inpaintStatus"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job Status"
data-zh_CN="生成状态">Job Status</span>
</li>
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="inpaintSeed"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job Seed"
data-zh_CN="图片随机数">Job Seed</span>
</li>
</ul>
</div>
<img class="card-img-bottom downloadable-img" id="inpaintImg">
</div>
</div>
</div>
</div>
<div class="card-body stable-diffusion-specific" id="card-txt" style="display:none">
<div class="row mb-3">
<div class="col-md-4">
<div class="card">
<div class="card-header" data-en_XX="1. Options" data-zh_CN="1. 选项">
1. Options
</div>
<div class="card-body">
<div class="row">
<button id="newTxt2ImgJob" class="btn btn-primary mb-3"
data-en_XX="Let's Go!" data-zh_CN="生成图片!">Let's Go!</button>
</div>
</div>
<img class="card-img-bottom" id="reference-img">
</div>
</div>
<div class="col-md-8">
<div class="card">
<div class="card-header" data-en_XX="2. Result" data-zh_CN="2. 结果">
2. Result
</div>
<div class="card-body">
<ul class="list-group">
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="txt2ImgJobUUID"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job UUID"
data-zh_CN="图片唯一识别码">Job UUID</span>
</li>
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="txt2ImgStatus"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job Status"
data-zh_CN="生成状态">Job Status</span>
</li>
<li
class="list-group-item d-flex justify-content-between align-items-center">
<span id="txt2ImgSeed"></span>
<span class="badge bg-primary rounded-pill" data-en_XX="Job Seed"
data-zh_CN="图片随机数">Job Seed</span>
</li>
</ul>
</div>
<img class="card-img-bottom downloadable-img" id="txt2ImgImg">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
<span data-en_XX="History" data-zh_CN="历史">History</span>
</div>
<div class="card-body">
<div class="input-group mb-3">
<label for="lookupUUID" class="input-group-text" data-en_XX="UUID (Optional)"
data-zh_CN="图片唯一识别码(选填)">UUID (Optional)</label>
<input type="text" class="form-control" id="lookupUUID" value="">
<button id="getJobHistory" class="btn btn-primary" data-en_XX="Get Job(s)" data-zh_CN="搜索历史">Get
Job(s)</button>
</div>
<div id="joblist"></div>
</div>
</div>
<div class="card mb-3">
<div class="card-header d-flex justify-content-between align-items-center">
<span data-en_XX="Get Inspirations from Others" data-zh_CN="从别人那里找灵感">Get Inspirations from
Others</span>
<button id="feelingLucky" class="btn btn-outline-secondary" data-en_XX="Change a batch"
data-zh_CN="换一批">Change a batch</button>
</div>
<div class="card-body">
<div id="othersJobList"></div>
</div>
</div>
</div>
<!-- CSS code -->
<style>
.private-card {
filter: blur(12px);
opacity: 0.7;
transition: filter 0.3s ease, opacity 0.3s ease;
}
.reveal-text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
color: white;
border-radius: 10px;
transition: filter 0.3s ease, opacity 0.3s ease;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
}
.flying-value {
position: absolute;
z-index: 9999;
font-size: 16px;
font-weight: bold;
color: #000;
}
.reveal-text svg {
width: 80px;
height: 80px;
fill: white;
filter: drop-shadow(2px 2px 4px rgba(126, 126, 126, 0.8));
}
</style>
<script src="{{ url_for('static',filename='jquery-3.6.1.min.js') }}"></script>
<script src="{{ url_for('static',filename='bootstrap.bundle.min.js') }}"></script>
<script src="{{ url_for('static',filename='jsketch.min.js') }}"></script>
<script src="{{ url_for('static',filename='jquery.sketchable.min.js') }}"></script>
<script src="{{ url_for('static',filename='jquery.sketchable.memento.min.js') }}"></script>
<script src="{{ url_for('static',filename='masonry.pkgd.min.js') }}"></script>
<script src="{{ url_for('static',filename='imagesloaded.pkgd.min.js') }}"></script>
<script>
function downloadImage(url, filename) {
var link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
}
function waitForImage(apikeyVal, uuidValue, jobType) {
// Wait until image is done
$.ajax({
type: 'POST',
url: '/get_jobs',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: JSON.stringify({ 'apikey': apikeyVal, 'uuid': uuidValue }),
success: function (response) {
console.log(response);
if (response.jobs.length == 1) {
if (jobType == 'txt') {
$('#txt2ImgStatus').html(response.jobs[0].status);
$('#txt2ImgSeed').html(response.jobs[0].seed);
$('#txt2ImgJobUUID').html(uuidValue);
if (response.jobs[0].status == "done") {
$('#txt2ImgImg').attr('src', response.jobs[0].img).attr('download', uuidValue + ".png");
return;
}
if (response.jobs[0].status == "failed") {
return;
}
} else if (jobType == 'img') {
$('#img2ImgStatus').html(response.jobs[0].status);
$('#img2ImgSeed').html(response.jobs[0].seed);
$('#img2ImgJobUUID').html(uuidValue);
if (response.jobs[0].status == "done") {
$('#img2ImgImg').attr('src', response.jobs[0].img).attr('download', uuidValue + ".png");
return;
}
if (response.jobs[0].status == "failed") {
return;
}
} else if (jobType == 'draw') {
$('#drawingStatus').html(response.jobs[0].status);
$('#drawingSeed').html(response.jobs[0].seed);
$('#drawingJobUUID').html(uuidValue);
if (response.jobs[0].status == "done") {
$('#drawingImg').attr('src', response.jobs[0].img).attr('download', uuidValue + ".png");
return;
}
if (response.jobs[0].status == "failed") {
return;
}
} else if (jobType == 'inpaint') {
$('#inpaintStatus').html(response.jobs[0].status);
$('#inpaintSeed').html(response.jobs[0].seed);
$('#inpaintJobUUID').html(uuidValue);
if (response.jobs[0].status == "done") {
$('#inpaintImg').attr('src', response.jobs[0].img).attr('download', uuidValue + ".png");
return;
}
if (response.jobs[0].status == "failed") {
return;
}
}
}
setTimeout(function () { waitForImage(apikeyVal, uuidValue, jobType); }, 1500); // refresh every 1.5 second
},
error: function (xhr, status, error) {
console.log(error);
setTimeout(function () { waitForImage(apikeyVal, uuidValue, jobType); }, 1500); // refresh every 1.5 second
}
});
}
function truncateText(text, maxLength) {
if (text.length <= maxLength) {
return text; // No truncation necessary
}
// Truncate the text and add an ellipsis (...) at the end
return text.slice(0, maxLength) + '...';
}
function parsePromptString(prompt, is_negative) {
// Split the prompt input by ","
var promptArray = prompt.replace(/[^\w\s,\p{Script=Han}]|\d/gu, '').split(/[,]/);
var result = "";
for (var i = 0; i < promptArray.length; i++) {
var trimmedString = promptArray[i].trim();
// Truncate the split string if it exceeds 100 words
if (trimmedString.split(' ').length > 100) {
trimmedString = trimmedString.split(' ').slice(0, 100).join(' ') + '...';
}
if (trimmedString.length > 0) {
// Wrap the string in a <span> element with required attributes and events
var spanElement = $("<span class='" + (is_negative ? "negative-prompt-example" : "prompt-example") + " badge rounded-pill text-bg-light' style='white-space:normal'>")
.text(trimmedString)
.css("cursor", "pointer");
result += spanElement[0].outerHTML;
}
}
return result;
}
// Helper function to get input field value or a default value if empty
function getInputValue(id, defaultValue) {
var value = $(id).val().trim();
return value !== '' ? value : defaultValue;
}
$(document).ready(function () {
$(document).on("click", ".negative-prompt-example, .prompt-example", function () {
var clickedText = $(this).text();
var inputId = ($(this).hasClass("negative-prompt-example")) ? "#negPrompt" : "#prompt";
var input = $(inputId);
var key = input.attr('id');
var currentValue = input.val();
if (currentValue && currentValue.includes(clickedText)) {
// If currentValue already contains clickedText, do nothing
return;
}
var newValue = (currentValue === "" ? "" : (currentValue + ", ")) + clickedText;
// Create a temporary span element to hold the flying value
var flyingValue = $('<span class="flying-value"></span>').text(clickedText);
// Append the temporary span to the body
$('body').append(flyingValue);
// Get the position of the clicked element
var clickedPosition = $(this).offset();
// Animate the flying value to the input field
flyingValue.css({
top: clickedPosition.top,
left: clickedPosition.left
}).animate({
top: input.offset().top,
left: input.offset().left,
opacity: 0
}, 500, function () {
// Remove the temporary span
flyingValue.remove();
});
input.val(newValue);
localStorage.setItem(key, newValue);
});
$(document).on({
mouseenter: function () {
$(this).toggleClass("text-bg-light text-bg-dark");
},
mouseleave: function () {
$(this).toggleClass("text-bg-light text-bg-dark");
}
}, ".negative-prompt-example, .prompt-example");
$('input[id]').on('input', function () {
var input = $(this);
var key = input.attr('id');
var value = input.val();
if (input.attr('type') == "checkbox") {
value = input.is(":checked");
}
localStorage.setItem(key, value);
}).each(function () {
var input = $(this);
var key = input.attr('id');
var value = localStorage.getItem(key);
if (input.attr('type') == "checkbox") {
input.prop('checked', value == "true");
} else if (value) {
input.val(value);
}
});
$(document).on('click', '.downloadable-img', function () {
var img = $(this);
downloadImage(img.attr('src'), img.attr('download') ? img.attr('download') : 'result.png');
});
// Cache variable to store the selected image data for img2img
var imageData = null;
$("#copy-txt-to-img, #copy-last-img").click(function () {
var data;
var sourceId = $(this).data("source-id");
data = $("#" + sourceId).attr("src");
if (data == null || data == "") {
alert("Nothing found from the result");
return;
}
imageData = data;
$("#reference-img").attr("src", imageData);
});
$('#txt2ImgJobUUID, #img2ImgJobUUID, #inpaintJobUUID').click(function () {
var jobUUID = $(this).html();
$('#lookupUUID').val(jobUUID);
});
$('#getJobHistory').click(function () {
var apikeyValue = $('#apiKey').val();
var uuidValue = $('#lookupUUID').val();
$.ajax({
type: 'POST',
url: '/get_jobs',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: JSON.stringify({ 'apikey': apikeyValue, 'uuid': uuidValue, 'type': 'txt,img,inpaint' }),
success: function (response) {
var jobsLength = response.jobs.length;
if (jobsLength == 0) {
$('#joblist').html("found nothing");
return;
}
var $joblist = $('#joblist');
var $grid = $('<div class="row"></div>');
$joblist.html($grid);
for (var i = 0; i < jobsLength; i++) {
var isPrivate = response.jobs[i].is_private;
var element = $("<div class='col col-sm-6 col-md-6 col-lg-4 mb-3'><div class='card'>" +
(response.jobs[i].img ? ("<img src='" + response.jobs[i].img + "' download='" + response.jobs[i].uuid + ".png' class='card-img-top downloadable-img'><div class='card-body'>") : "") +
"<ul class='list-group list-group-flush'>" +
"<li class='list-group-item'>status: " + response.jobs[i].status + "</li>" +
"<li class='list-group-item'>prompt: " + parsePromptString(response.jobs[i].prompt, false) + "</li>" +
"<li class='list-group-item'>neg prompt: " + parsePromptString(response.jobs[i].neg_prompt, true) + "</li>" +
"<li class='list-group-item'>seed: " + response.jobs[i].seed + "</li>" +
"<li class='list-group-item'>uuid: " + response.jobs[i].uuid + "</li>" +
"<li class='list-group-item'>w x h: " + response.jobs[i].width + " x " + response.jobs[i].height + "</li>" +
"</ul></div>" +
(response.jobs[i].ref_img ? ("<img src='" + response.jobs[i].ref_img + "' class='card-img-bottom'>") : "") +
"</div></div>");
// Add event handler for click to toggle blurriness
if (isPrivate === 1) {
element.find('.card').addClass('private-card');
element.append("<div class='reveal-text'><svg viewBox='0 0 16 16'><path d='M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z'/></svg></div>");
element.on('click', function (event) {
if ($(event.target).hasClass('negative-prompt-example') || $(event.target).hasClass('prompt-example') || $(event.target).hasClass('downloadable-img')) {
return; // Ignore the click event for those elements
}
$(this).find('.card').toggleClass('private-card');
$(this).find('.reveal-text').toggle();
});
}
$grid.append(element);
};
$grid.imagesLoaded().progress(function () {
$grid.masonry({
itemSelector: '.col',
columnWidth: '.col',
percentPosition: true
});
});
},
error: function (xhr, status, error) {
$('#joblist').html("try again later");
}
});
});
$('#feelingLucky').click(function () {
$.ajax({
type: 'GET',
url: '/random_jobs',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (response) {
var jobsLength = response.jobs.length;
if (jobsLength == 0) {
$('#othersJobList').html("found nothing");
return;
}
var $joblist = $('#othersJobList');
var $grid = $('<div class="row"></div>');
$joblist.html($grid);
for (var i = 0; i < jobsLength; i++) {
var element = ("<div class='col col-sm-6 col-md-6 col-lg-4 mb-3'><div class='card'>" +
(response.jobs[i].img ? ("<img src='" + response.jobs[i].img + "' class='card-img-top downloadable-img'><div class='card-body'>") : "") +
"<ul class='list-group list-group-flush'>" +
"<li class='list-group-item'>prompt: " + parsePromptString(response.jobs[i].prompt, false) + "</li>" +
"<li class='list-group-item'>neg prompt: " + parsePromptString(response.jobs[i].neg_prompt, true) + "</li>" +
"<li class='list-group-item'>seed: " + response.jobs[i].seed + "</li>" +
"<li class='list-group-item'>w x h: " + response.jobs[i].width + " x " + response.jobs[i].height + "</li>" +
"</ul>" +
"</div></div></div>")
$grid.append(element);
};
$grid.imagesLoaded().progress(function () {
$grid.masonry({
itemSelector: '.col',
columnWidth: '.col',
percentPosition: true
});
});
},
error: function (xhr, status, error) {
$('#othersJobList').html(error);
}
});
});
$('#feelingLucky').click();
$("#upload-img").click(function () {
var input = $("<input type='file' accept='image/*'>").on("change", function () {
var reader = new FileReader();
reader.onload = function (e) {
imageData = e.target.result;
$("#reference-img").attr("src", imageData);
};
reader.readAsDataURL(this.files[0]);
});
input.click();
});
var activeLink = localStorage.getItem('activeLink') || $('.stable-diffusion .nav-link:first').attr('href');
// Set the active link as the current active link
$('.stable-diffusion .nav-link[href="' + activeLink + '"]').addClass('active');
$(activeLink).show();
$(".stable-diffusion .nav-link").click(function (e) {
e.preventDefault();
var target = $(this).attr("href"); // get the href value of the clicked link
// hide all card divs and show the corresponding one
$(".stable-diffusion-specific").hide();
$(".stable-diffusion .nav-link").removeClass("active");
$(this).addClass("active");
$(target).show();
localStorage.setItem('activeLink', target);
});
function submitJob(formData, uuidSelector, statusSelector, jobType) {
$.ajax({
type: 'POST',
url: '/add_job',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: JSON.stringify(formData),
success: function (response) {
if (response.uuid) {
$(uuidSelector).html(response.uuid);
}
$(statusSelector).html('Submitting new job..');
waitForImage(formData.apikey, response.uuid, jobType);
},
error: function (xhr, status, error) {
// Handle error response
console.log(xhr.responseText);
$(statusSelector).html('Failed');
}
});
}
$('#newTxt2ImgJob').click(function (e) {
e.preventDefault(); // Prevent the default form submission
// Validate input field values
var promptVal = getInputValue('#prompt', '');
var guidanceScaleVal = parseFloat(getInputValue('#inputGuidanceScale', '7.5'));
var stepsVal = parseInt(getInputValue('#inputSteps', '50'));
var widthVal = parseInt(getInputValue('#inputWidth', '512'));
var heightVal = parseInt(getInputValue('#inputHeight', '512'));
if (promptVal === '') {
alert("Missing prompt!");
return;
}
if (guidanceScaleVal < 1 || guidanceScaleVal > 30) {
alert("Guidance scale must be between 1 and 30!");
return;
}
if (widthVal < 8 || widthVal > 960 || widthVal % 8 !== 0) {
alert("Width must be between 8 and 960 and divisible by 8!");
return;
}
if (heightVal < 8 || heightVal > 960 || heightVal % 8 !== 0) {
alert("Height must be between 8 and 960 and divisible by 8!");
return;
}
var apikeyVal = $('#apiKey').val();
var negPromptVal = $('#negPrompt').val();
var seedVal = getInputValue('#inputSeed', '0');
var formData = {
'apikey': apikeyVal,
'type': 'txt',
'prompt': promptVal,
'seed': seedVal,
'steps': stepsVal,
'width': widthVal,
'height': heightVal,
'lang': $("#language option:selected").val(),
'guidance_scale': guidanceScaleVal,
'neg_prompt': negPromptVal,
'is_private': $('#isPrivate').is(":checked") ? 1 : 0
};
submitJob(formData, '#txt2ImgJobUUID', '#txt2ImgStatus', 'txt');
});
$('#newImg2ImgJob').click(function (e) {
e.preventDefault(); // Prevent the default form submission
if (imageData == null) {
alert("No image cached");
return;
}
// Validate input field values
var promptVal = getInputValue('#prompt', '');
var guidanceScaleVal = parseFloat(getInputValue('#inputGuidanceScale', '25.0'));
var stepsVal = parseInt(getInputValue('#inputSteps', '50'));
var widthVal = parseInt(getInputValue('#inputWidth', '512'));
var heightVal = parseInt(getInputValue('#inputHeight', '512'));
if (promptVal === '') {
alert("Missing prompt!");
return;
}
if (guidanceScaleVal < 1 || guidanceScaleVal > 30) {
alert("Guidance scale must be between 1 and 30!");
return;
}
if (widthVal < 8 || widthVal > 960 || widthVal % 8 !== 0) {
alert("Width must be between 8 and 960 and divisible by 8!");
return;
}
if (heightVal < 8 || heightVal > 960 || heightVal % 8 !== 0) {
alert("Height must be between 8 and 960 and divisible by 8!");
return;
}
var apikeyVal = $('#apiKey').val();
var negPromptVal = $('#negPrompt').val();
var seedVal = $('#inputSeed').val();
if (seedVal == "0" || seedVal == "") {
seedVal = "0";
}
var strengthVal = parseFloat(getInputValue('#inputStrength', '0.5'));
if (strengthVal < 0 || strengthVal > 1) {
alert("Strength must be between 0 and 1!");
return;
}
var formData = {
'apikey': apikeyVal,
'type': 'img',
'ref_img': imageData,
'prompt': promptVal,
'seed': seedVal,
'steps': stepsVal,
'width': widthVal,
'height': heightVal,
'lang': $("#language option:selected").val(),
'guidance_scale': guidanceScaleVal,
'strength': strengthVal,
'neg_prompt': negPromptVal,
'is_private': $('#isPrivate').is(":checked") ? 1 : 0
};
submitJob(formData, '#img2ImgJobUUID', '#img2ImgStatus', 'img');
});
$('#newDrawingJob').click(function (e) {
e.preventDefault(); // Prevent the default form submission
// Validate input field values
var promptVal = getInputValue('#prompt', '');
var guidanceScaleVal = parseFloat(getInputValue('#inputGuidanceScale', '25.0'));
var stepsVal = parseInt(getInputValue('#inputSteps', '50'));
var widthVal = parseInt(getInputValue('#inputWidth', '512'));
var heightVal = parseInt(getInputValue('#inputHeight', '512'));
if (promptVal === '') {
alert("Missing prompt!");
return;
}
if (guidanceScaleVal < 1 || guidanceScaleVal > 30) {
alert("Guidance scale must be between 1 and 30!");
return;
}
if (widthVal < 8 || widthVal > 960 || widthVal % 8 !== 0) {
alert("Width must be between 8 and 960 and divisible by 8!");
return;
}
if (heightVal < 8 || heightVal > 960 || heightVal % 8 !== 0) {
alert("Height must be between 8 and 960 and divisible by 8!");
return;
}
var apikeyVal = $('#apiKey').val();
var negPromptVal = $('#negPrompt').val();
var seedVal = $('#inputSeed').val();
if (seedVal == "0" || seedVal == "") {
seedVal = "0";
}
var strengthVal = parseFloat(getInputValue('#inputStrength', '0.5'));
if (strengthVal < 0 || strengthVal > 1) {
alert("Strength must be between 0 and 1!");
return;
}
var canvas = $('#drawing-img-mask')[0];
var ctx = canvas.getContext('2d');
var sketchImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// Loop through the pixels and change the colors
for (var i = 0; i < sketchImageData.data.length; i += 4) {
if (sketchImageData.data[i + 3] == 0) { // If pixel is transparent, change to white
sketchImageData.data[i] = 255;
sketchImageData.data[i + 1] = 255;
sketchImageData.data[i + 2] = 255;
sketchImageData.data[i + 3] = 255;
} else { // If pixel is not transparent, change to black
sketchImageData.data[i] = 0;
sketchImageData.data[i + 1] = 0;
sketchImageData.data[i + 2] = 0;
sketchImageData.data[i + 3] = 255;
}
}
var tempCanvas = document.createElement('canvas'); // Create a new canvas element
tempCanvas.width = canvas.width; // Set the width of the new canvas to match the original canvas
tempCanvas.height = canvas.height; // Set the height of the new canvas to match the original canvas
var tempCtx = tempCanvas.getContext('2d');
tempCtx.putImageData(sketchImageData, 0, 0); // Put modified image data onto the new canvas
var drawingSketchImg = tempCanvas.toDataURL(); // Get the modified base64-encoded image data
var formData = {
'apikey': apikeyVal,
'type': 'img',
'ref_img': drawingSketchImg,
'prompt': promptVal,
'seed': seedVal,
'steps': stepsVal,
'width': widthVal,
'height': heightVal,
'lang': $("#language option:selected").val(),
'guidance_scale': guidanceScaleVal,
'strength': strengthVal,
'neg_prompt': negPromptVal,
'is_private': $('#isPrivate').is(":checked") ? 1 : 0
};
submitJob(formData, '#drawingJobUUID', '#drawingStatus', 'draw');
});
$('#newInpaintingJob').click(function (e) {
e.preventDefault(); // Prevent the default form submission
if (inpaintOriginalImg == null) {
alert("No image cached");
return;
}
var canvas = $('#inpaint-img-mask')[0];
var ctx = canvas.getContext('2d');
var maskImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// Loop through the pixels and change the colors
for (var i = 0; i < maskImageData.data.length; i += 4) {
if (maskImageData.data[i + 3] == 0) { // If pixel is transparent, change to black
maskImageData.data[i] = 0;
maskImageData.data[i + 1] = 0;
maskImageData.data[i + 2] = 0;
maskImageData.data[i + 3] = 255;
} else { // If pixel is not transparent, change to white
maskImageData.data[i] = 255;
maskImageData.data[i + 1] = 255;
maskImageData.data[i + 2] = 255;
maskImageData.data[i + 3] = 255;
}
}
var tempCanvas = document.createElement('canvas'); // Create a new canvas element
tempCanvas.width = canvas.width; // Set the width of the new canvas to match the original canvas
tempCanvas.height = canvas.height; // Set the height of the new canvas to match the original canvas
var tempCtx = tempCanvas.getContext('2d');
tempCtx.putImageData(maskImageData, 0, 0); // Put modified image data onto the new canvas
var inpaintMaskImg = tempCanvas.toDataURL(); // Get the modified base64-encoded image data
// Validate input field values
var apikeyVal = $('#apiKey').val();
var promptVal = getInputValue('#prompt', '');
var negPromptVal = $('#negPrompt').val();
var seedVal = getInputValue('#inputSeed', '0');
var guidanceScaleVal = parseFloat(getInputValue('#inputGuidanceScale', '25.0'));
var stepsVal = parseInt(getInputValue('#inputSteps', '50'));
var widthVal = parseInt(getInputValue('#inputWidth', '512'));
var heightVal = parseInt(getInputValue('#inputHeight', '512'));
if (promptVal === '') {
alert("Missing prompt!");
return;
}
if (guidanceScaleVal < 1 || guidanceScaleVal > 30) {
alert("Guidance scale must be between 1 and 30!");
return;
}
if (widthVal < 8 || widthVal > 960 || widthVal % 8 !== 0) {
alert("Width must be between 8 and 960 and divisible by 8!");
return;
}
if (heightVal < 8 || heightVal > 960 || heightVal % 8 !== 0) {
alert("Height must be between 8 and 960 and divisible by 8!");
return;
}
var formData = {
'apikey': apikeyVal,
'type': 'inpaint',
'ref_img': inpaintOriginalImg,
'mask_img': inpaintMaskImg,
'prompt': promptVal,
'seed': seedVal,
'steps': stepsVal,
'width': widthVal,
'height': heightVal,
'lang': $("#language option:selected").val(),
'guidance_scale': guidanceScaleVal,
'neg_prompt': negPromptVal,
'is_private': $('#isPrivate').is(":checked") ? 1 : 0
};
submitJob(formData, '#inpaintJobUUID', '#inpaintStatus', 'inpaint');
});
// Define the function to update the text based on the selected language
function updateText(language) {
$("[data-" + language + "]").each(function () {
$(this).text($(this).data(language.toLowerCase()));
});
}
// Listen for changes to the select element
$("#language").change(function () {
// Get the newly selected value
var newLanguage = $(this).val();
// Store the selected value in cache
localStorage.setItem("selectedLanguage", newLanguage);
// Update the text based on the selected language
updateText(newLanguage);
});
// Get the selected value from cache (if it exists)
var cachedLanguage = localStorage.getItem("selectedLanguage");
if (cachedLanguage) {
// Set the selected value
$("#language").val(cachedLanguage);
// Update the text based on the selected language
updateText(cachedLanguage);
}
// Cache variable to store the selected image data for inpainting
var inpaintOriginalImg = null;
function setInpaintOriginalImg(data) {
inpaintOriginalImg = data;
$("#inpaint-img").attr("src", inpaintOriginalImg);
$("#inpaint-img").trigger("change");
}
$("#copy-txt-to-img-inpaint").click(function () {
var data = $("#txt2ImgImg").attr("src");
if (!data) {
alert("Nothing found from txt-to-img result");
return;
}
setInpaintOriginalImg(data);
});
$("#copy-last-img-inpaint").click(function () {
var data = $("#img2ImgImg").attr("src");
if (!data) {
alert("Nothing found from img-to-img result");
return;
}
setInpaintOriginalImg(data);
});
$("#upload-img-inpaint").click(function () {
var input = $("<input type='file' accept='image/*'>");
input.on("change", function () {
var reader = new FileReader();
reader.onload = function (e) {
setInpaintOriginalImg(e.target.result);
var img = new Image();
img.src = inpaintOriginalImg;
img.onload = function () {
$("#inpaint-img").trigger("change");
};
};
reader.readAsDataURL(input[0].files[0]);
});
input.click();
});
$("#inpaint-strike-size").on("input", function () {
var strikeSize = $(this).val();
$('#inpaint-range-value').val(strikeSize);
$("#inpaint-strike-size").trigger("inputChange", strikeSize);
});
$('#inpaint-range-value').val($('#inpaint-strike-size').val());
$("#inpaint-img").on("change", function () {
var src = $(this).attr("src");
$("#inpaint-img-for-mask").attr("src", src);
$("#inpaint-mask-canvas-container").html("<canvas id='inpaint-img-mask' width=" + $(this).width() + " height=" + $(this).height() + ">");
var options = {
graphics: {
firstPointSize: 0,
lineWidth: $("#inpaint-strike-size").val(),
strokeStyle: 'black',
}
};
$sketcher = $('#inpaint-img-mask').sketchable(options);
$('#inpaint-reset').click(function () {
$sketcher.sketchable('clear');
});
$('#inpaint-undo').click(function () {
$sketcher.sketchable('memento.undo');
});
$('#inpaint-redo').click(function () {
$sketcher.sketchable('memento.redo');
});
$("#inpaint-strike-size").on("inputChange", function (e, strikeSize) {
$sketcher.sketchable('config', { graphics: { lineWidth: strikeSize } });
});
});
$("#drawing-strike-size").on("input", function () {
var strikeSize = $(this).val();
$('#drawing-range-value').val(strikeSize);
$("#drawing-strike-size").trigger("inputChange", strikeSize);
});
$('#drawing-range-value').val($('#drawing-strike-size').val());
$(document).on('change', "#inputWidth, #inputHeight", function () {
var widthVal = parseInt(getInputValue('#inputWidth', '512'));
var heightVal = parseInt(getInputValue('#inputHeight', '512'));
if (widthVal < 8 || widthVal > 960 || widthVal % 8 !== 0) {
alert("Width must be between 8 and 960 and divisible by 8!");
return;
}
if (heightVal < 8 || heightVal > 960 || heightVal % 8 !== 0) {
alert("Height must be between 8 and 960 and divisible by 8!");
return;
}
$("#drawing-mask-canvas-container").html("<canvas id='drawing-img-mask' width=" + widthVal + " height=" + heightVal + ">");
var options = {
graphics: {
firstPointSize: 0,
lineWidth: $("#drawing-strike-size").val(),
strokeStyle: 'black',
}
};
$sketcher = $('#drawing-img-mask').sketchable(options);
$('#drawing-reset').click(function () {
$sketcher.sketchable('clear');
});
$('#drawing-undo').click(function () {
$sketcher.sketchable('memento.undo');
});
$('#drawing-redo').click(function () {
$sketcher.sketchable('memento.redo');
});
$("#drawing-strike-size").on("inputChange", function (e, strikeSize) {
$sketcher.sketchable('config', { graphics: { lineWidth: strikeSize } });
});
});
$('#inputWidth').trigger('change');
});
</script>
</body>
</html>