Upstream easydiffusion/easydiffusion state of the touched files before cb0a714210
This commit is contained in:
commit
20efbbb3db
1 file changed
+910
@@ -0,0 +1,910 @@
|
||||
"use strict" // Opt in to a restricted variant of JavaScript
|
||||
|
||||
const EXT_REGEX = /(?:\.([^.]+))?$/
|
||||
const TEXT_EXTENSIONS = ["txt", "json"]
|
||||
const IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "bmp", "tiff", "tif", "tga", "webp"]
|
||||
|
||||
function parseBoolean(stringValue) {
|
||||
if (typeof stringValue === "boolean") {
|
||||
return stringValue
|
||||
}
|
||||
if (typeof stringValue === "number") {
|
||||
return stringValue !== 0
|
||||
}
|
||||
if (typeof stringValue !== "string") {
|
||||
return false
|
||||
}
|
||||
switch (stringValue?.toLowerCase()?.trim()) {
|
||||
case "true":
|
||||
case "yes":
|
||||
case "on":
|
||||
case "1":
|
||||
return true
|
||||
|
||||
case "false":
|
||||
case "no":
|
||||
case "off":
|
||||
case "0":
|
||||
case "none":
|
||||
case null:
|
||||
case undefined:
|
||||
return false
|
||||
}
|
||||
try {
|
||||
return Boolean(JSON.parse(stringValue))
|
||||
} catch {
|
||||
return Boolean(stringValue)
|
||||
}
|
||||
}
|
||||
|
||||
// keep in sync with `ui/easydiffusion/utils/save_utils.py`
|
||||
const TASK_MAPPING = {
|
||||
prompt: {
|
||||
name: "Prompt",
|
||||
setUI: (prompt) => {
|
||||
promptField.value = prompt
|
||||
},
|
||||
readUI: () => promptField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
negative_prompt: {
|
||||
name: "Negative Prompt",
|
||||
setUI: (negative_prompt) => {
|
||||
negativePromptField.value = negative_prompt
|
||||
},
|
||||
readUI: () => negativePromptField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
active_tags: {
|
||||
name: "Image Modifiers",
|
||||
setUI: (active_tags) => {
|
||||
refreshModifiersState(active_tags)
|
||||
},
|
||||
readUI: () => activeTags.map((x) => x.name),
|
||||
parse: (val) => val,
|
||||
},
|
||||
inactive_tags: {
|
||||
name: "Inactive Image Modifiers",
|
||||
setUI: (inactive_tags) => {
|
||||
refreshInactiveTags(inactive_tags)
|
||||
},
|
||||
readUI: () => activeTags.filter((tag) => tag.inactive === true).map((x) => x.name),
|
||||
parse: (val) => val,
|
||||
},
|
||||
width: {
|
||||
name: "Width",
|
||||
setUI: (width) => {
|
||||
try {
|
||||
addImageSizeOption(width)
|
||||
widthField.value = width
|
||||
widthField.dispatchEvent(new Event("change"))
|
||||
} catch (e) {
|
||||
console.log("Invalid width value", width)
|
||||
}
|
||||
},
|
||||
readUI: () => parseInt(widthField.value),
|
||||
parse: (val) => parseInt(val),
|
||||
},
|
||||
height: {
|
||||
name: "Height",
|
||||
setUI: (height) => {
|
||||
try {
|
||||
addImageSizeOption(height)
|
||||
heightField.value = height
|
||||
heightField.dispatchEvent(new Event("change"))
|
||||
} catch (e) {
|
||||
console.log("Invalid height value", height)
|
||||
}
|
||||
},
|
||||
readUI: () => parseInt(heightField.value),
|
||||
parse: (val) => parseInt(val),
|
||||
},
|
||||
seed: {
|
||||
name: "Seed",
|
||||
setUI: (seed) => {
|
||||
if (!seed) {
|
||||
randomSeedField.checked = true
|
||||
seedField.disabled = true
|
||||
seedField.value = 0
|
||||
return
|
||||
}
|
||||
randomSeedField.checked = false
|
||||
randomSeedField.dispatchEvent(new Event("change")) // let plugins know that the state of the random seed toggle changed
|
||||
seedField.disabled = false
|
||||
seedField.value = seed
|
||||
},
|
||||
readUI: () => parseInt(seedField.value), // just return the value the user is seeing in the UI
|
||||
parse: (val) => parseInt(val),
|
||||
},
|
||||
num_inference_steps: {
|
||||
name: "Steps",
|
||||
setUI: (num_inference_steps) => {
|
||||
numInferenceStepsField.value = num_inference_steps
|
||||
},
|
||||
readUI: () => parseInt(numInferenceStepsField.value),
|
||||
parse: (val) => parseInt(val),
|
||||
},
|
||||
guidance_scale: {
|
||||
name: "Guidance Scale",
|
||||
setUI: (guidance_scale) => {
|
||||
guidanceScaleField.value = guidance_scale
|
||||
updateGuidanceScaleSlider()
|
||||
},
|
||||
readUI: () => parseFloat(guidanceScaleField.value),
|
||||
parse: (val) => parseFloat(val),
|
||||
},
|
||||
distilled_guidance_scale: {
|
||||
name: "Distilled Guidance",
|
||||
setUI: (distilled_guidance_scale) => {
|
||||
distilledGuidanceScaleField.value = distilled_guidance_scale
|
||||
updateDistilledGuidanceScaleSlider()
|
||||
},
|
||||
readUI: () => parseFloat(distilledGuidanceScaleField.value),
|
||||
parse: (val) => parseFloat(val),
|
||||
},
|
||||
prompt_strength: {
|
||||
name: "Prompt Strength",
|
||||
setUI: (prompt_strength) => {
|
||||
promptStrengthField.value = prompt_strength
|
||||
updatePromptStrengthSlider()
|
||||
},
|
||||
readUI: () => parseFloat(promptStrengthField.value),
|
||||
parse: (val) => parseFloat(val),
|
||||
},
|
||||
|
||||
init_image: {
|
||||
name: "Initial Image",
|
||||
setUI: (init_image) => {
|
||||
initImagePreview.src = init_image
|
||||
},
|
||||
readUI: () => initImagePreview.src,
|
||||
parse: (val) => val,
|
||||
},
|
||||
ref_images: {
|
||||
name: "Reference Images",
|
||||
setUI: (ref_images) => {
|
||||
if (Array.isArray(ref_images) && ref_images.length > 0) {
|
||||
refImages = ref_images.slice()
|
||||
renderRefImagesList()
|
||||
} else {
|
||||
clearAllRefImages()
|
||||
}
|
||||
},
|
||||
readUI: () => (refImages.length > 0 ? refImages.slice() : undefined),
|
||||
parse: (val) => (Array.isArray(val) ? val : undefined),
|
||||
},
|
||||
mask: {
|
||||
name: "Mask",
|
||||
setUI: (mask) => {
|
||||
setTimeout(() => {
|
||||
// add a delay to insure this happens AFTER the main image loads (which reloads the inpainter)
|
||||
imageInpainter.setImg(mask)
|
||||
}, 250)
|
||||
maskSetting.checked = Boolean(mask)
|
||||
},
|
||||
readUI: () => (maskSetting.checked ? imageInpainter.getImg() : undefined),
|
||||
parse: (val) => val,
|
||||
},
|
||||
preserve_init_image_color_profile: {
|
||||
name: "Preserve Color Profile",
|
||||
setUI: (preserve_init_image_color_profile) => {
|
||||
applyColorCorrectionField.checked = parseBoolean(preserve_init_image_color_profile)
|
||||
},
|
||||
readUI: () => applyColorCorrectionField.checked,
|
||||
parse: (val) => parseBoolean(val),
|
||||
},
|
||||
|
||||
use_face_correction: {
|
||||
name: "Use Face Correction",
|
||||
setUI: (use_face_correction) => {
|
||||
const oldVal = gfpganModelField.value
|
||||
console.log("use face correction", use_face_correction)
|
||||
if (use_face_correction == null || use_face_correction == "None") {
|
||||
gfpganModelField.disabled = true
|
||||
useFaceCorrectionField.checked = false
|
||||
} else {
|
||||
gfpganModelField.value = getModelPath(use_face_correction, [".pth"])
|
||||
if (gfpganModelField.value) {
|
||||
// Is a valid value for the field.
|
||||
useFaceCorrectionField.checked = true
|
||||
gfpganModelField.disabled = false
|
||||
} else {
|
||||
// Not a valid value, restore the old value and disable the filter.
|
||||
gfpganModelField.disabled = true
|
||||
gfpganModelField.value = oldVal
|
||||
useFaceCorrectionField.checked = false
|
||||
}
|
||||
}
|
||||
|
||||
//useFaceCorrectionField.checked = parseBoolean(use_face_correction)
|
||||
},
|
||||
readUI: () => (useFaceCorrectionField.checked ? gfpganModelField.value : undefined),
|
||||
parse: (val) => val,
|
||||
},
|
||||
use_upscale: {
|
||||
name: "Use Upscaling",
|
||||
setUI: (use_upscale) => {
|
||||
const oldVal = upscaleModelField.value
|
||||
upscaleModelField.value = getModelPath(use_upscale, [".pth"])
|
||||
if (upscaleModelField.value) {
|
||||
// Is a valid value for the field.
|
||||
useUpscalingField.checked = true
|
||||
upscaleModelField.disabled = false
|
||||
upscaleAmountField.disabled = false
|
||||
} else {
|
||||
// Not a valid value, restore the old value and disable the filter.
|
||||
upscaleModelField.disabled = true
|
||||
upscaleAmountField.disabled = true
|
||||
upscaleModelField.value = oldVal
|
||||
useUpscalingField.checked = false
|
||||
}
|
||||
},
|
||||
readUI: () => (useUpscalingField.checked ? upscaleModelField.value : undefined),
|
||||
parse: (val) => val,
|
||||
},
|
||||
upscale_amount: {
|
||||
name: "Upscale By",
|
||||
setUI: (upscale_amount) => {
|
||||
upscaleAmountField.value = upscale_amount
|
||||
},
|
||||
readUI: () => upscaleAmountField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
latent_upscaler_steps: {
|
||||
name: "Latent Upscaler Steps",
|
||||
setUI: (latent_upscaler_steps) => {
|
||||
latentUpscalerStepsField.value = latent_upscaler_steps
|
||||
},
|
||||
readUI: () => latentUpscalerStepsField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
sampler_name: {
|
||||
name: "Sampler",
|
||||
setUI: (sampler_name) => {
|
||||
samplerField.value = sampler_name
|
||||
},
|
||||
readUI: () => samplerField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
scheduler_name: {
|
||||
name: "Scheduler",
|
||||
setUI: (scheduler_name) => {
|
||||
schedulerField.value = scheduler_name
|
||||
},
|
||||
readUI: () => schedulerField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
use_stable_diffusion_model: {
|
||||
name: "Stable Diffusion model",
|
||||
setUI: (use_stable_diffusion_model) => {
|
||||
const oldVal = stableDiffusionModelField.value
|
||||
|
||||
use_stable_diffusion_model = getModelPath(use_stable_diffusion_model, [".ckpt", ".safetensors"])
|
||||
stableDiffusionModelField.value = use_stable_diffusion_model
|
||||
|
||||
if (!stableDiffusionModelField.value) {
|
||||
stableDiffusionModelField.value = oldVal
|
||||
}
|
||||
},
|
||||
readUI: () => stableDiffusionModelField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
clip_skip: {
|
||||
name: "Clip Skip",
|
||||
setUI: (value) => {
|
||||
clip_skip.checked = value
|
||||
},
|
||||
readUI: () => clip_skip.checked,
|
||||
parse: (val) => Boolean(val),
|
||||
},
|
||||
tiling: {
|
||||
name: "Tiling",
|
||||
setUI: (val) => {
|
||||
if (val === null || val === "None") {
|
||||
tilingField.value = "none"
|
||||
} else {
|
||||
tilingField.value = val
|
||||
}
|
||||
},
|
||||
readUI: () => tilingField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
use_vae_model: {
|
||||
name: "VAE model",
|
||||
setUI: (use_vae_model) => {
|
||||
const oldVal = vaeModelField.value
|
||||
use_vae_model =
|
||||
use_vae_model === undefined || use_vae_model === null || use_vae_model === "None" ? "" : use_vae_model
|
||||
|
||||
if (use_vae_model !== "") {
|
||||
use_vae_model = getModelPath(use_vae_model, [".vae.pt", ".ckpt"])
|
||||
use_vae_model = use_vae_model !== "" ? use_vae_model : oldVal
|
||||
}
|
||||
vaeModelField.value = use_vae_model
|
||||
},
|
||||
readUI: () => vaeModelField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
use_controlnet_model: {
|
||||
name: "ControlNet model",
|
||||
setUI: (use_controlnet_model) => {
|
||||
controlnetModelField.value = getModelPath(use_controlnet_model, [".pth", ".safetensors"])
|
||||
},
|
||||
readUI: () => controlnetModelField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
control_filter_to_apply: {
|
||||
name: "ControlNet Filter",
|
||||
setUI: (control_filter_to_apply) => {
|
||||
controlImageFilterField.value = control_filter_to_apply
|
||||
},
|
||||
readUI: () => controlImageFilterField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
control_alpha: {
|
||||
name: "ControlNet Strength",
|
||||
setUI: (control_alpha) => {
|
||||
control_alpha = control_alpha || 1.0
|
||||
controlAlphaField.value = control_alpha
|
||||
updateControlAlphaSlider()
|
||||
},
|
||||
readUI: () => parseFloat(controlAlphaField.value),
|
||||
parse: (val) => val === null ? 1.0 : parseFloat(val),
|
||||
},
|
||||
use_lora_model: {
|
||||
name: "LoRA model",
|
||||
setUI: (use_lora_model) => {
|
||||
let modelPaths = []
|
||||
use_lora_model = use_lora_model === null ? "" : use_lora_model
|
||||
use_lora_model = Array.isArray(use_lora_model) ? use_lora_model : [use_lora_model]
|
||||
use_lora_model.forEach((m) => {
|
||||
if (m.includes("models\\lora\\")) {
|
||||
m = m.split("models\\lora\\")[1]
|
||||
} else if (m.includes("models\\\\lora\\\\")) {
|
||||
m = m.split("models\\\\lora\\\\")[1]
|
||||
} else if (m.includes("models/lora/")) {
|
||||
m = m.split("models/lora/")[1]
|
||||
}
|
||||
m = m.replaceAll("\\\\", "/")
|
||||
m = getModelPath(m, [".ckpt", ".safetensors"])
|
||||
modelPaths.push(m)
|
||||
})
|
||||
loraModelField.modelNames = modelPaths
|
||||
},
|
||||
readUI: () => {
|
||||
return loraModelField.modelNames
|
||||
},
|
||||
parse: (val) => {
|
||||
val = !val || val === "None" ? "" : val
|
||||
if (typeof val === "string" && val.includes(",")) {
|
||||
val = val.split(",")
|
||||
val = val.map((v) => v.trim())
|
||||
val = val.map((v) => v.replaceAll("\\", "\\\\"))
|
||||
val = val.map((v) => v.replaceAll('"', ""))
|
||||
val = val.map((v) => v.replaceAll("'", ""))
|
||||
val = val.map((v) => '"' + v + '"')
|
||||
val = "[" + val + "]"
|
||||
val = JSON.parse(val)
|
||||
}
|
||||
val = Array.isArray(val) ? val : [val]
|
||||
return val
|
||||
},
|
||||
},
|
||||
lora_alpha: {
|
||||
name: "LoRA Strength",
|
||||
setUI: (lora_alpha) => {
|
||||
lora_alpha = Array.isArray(lora_alpha) ? lora_alpha : [lora_alpha]
|
||||
loraModelField.modelWeights = lora_alpha
|
||||
},
|
||||
readUI: () => {
|
||||
return loraModelField.modelWeights
|
||||
},
|
||||
parse: (val) => {
|
||||
if (typeof val === "string" && val.includes(",")) {
|
||||
val = "[" + val.replaceAll("'", '"') + "]"
|
||||
val = JSON.parse(val)
|
||||
}
|
||||
val = Array.isArray(val) ? val : [val]
|
||||
val = val.map((e) => parseFloat(e))
|
||||
return val
|
||||
},
|
||||
},
|
||||
use_text_encoder_model: {
|
||||
name: "Text Encoder model",
|
||||
setUI: (use_text_encoder_model) => {
|
||||
let modelPaths = []
|
||||
use_text_encoder_model = use_text_encoder_model === null ? "" : use_text_encoder_model
|
||||
use_text_encoder_model = Array.isArray(use_text_encoder_model) ? use_text_encoder_model : [use_text_encoder_model]
|
||||
use_text_encoder_model.forEach((m) => {
|
||||
if (m.includes("models\\text-encoder\\")) {
|
||||
m = m.split("models\\text-encoder\\")[1]
|
||||
} else if (m.includes("models\\\\text-encoder\\\\")) {
|
||||
m = m.split("models\\\\text-encoder\\\\")[1]
|
||||
} else if (m.includes("models/text-encoder/")) {
|
||||
m = m.split("models/text-encoder/")[1]
|
||||
}
|
||||
m = m.replaceAll("\\\\", "/")
|
||||
m = getModelPath(m, [".safetensors", ".sft"])
|
||||
modelPaths.push(m)
|
||||
})
|
||||
textEncoderModelField.modelNames = modelPaths
|
||||
},
|
||||
readUI: () => {
|
||||
return textEncoderModelField.modelNames
|
||||
},
|
||||
parse: (val) => {
|
||||
val = !val || val === "None" ? "" : val
|
||||
if (typeof val === "string" && val.includes(",")) {
|
||||
val = val.split(",")
|
||||
val = val.map((v) => v.trim())
|
||||
val = val.map((v) => v.replaceAll("\\", "\\\\"))
|
||||
val = val.map((v) => v.replaceAll('"', ""))
|
||||
val = val.map((v) => v.replaceAll("'", ""))
|
||||
val = val.map((v) => '"' + v + '"')
|
||||
val = "[" + val + "]"
|
||||
val = JSON.parse(val)
|
||||
}
|
||||
val = Array.isArray(val) ? val : [val]
|
||||
return val
|
||||
},
|
||||
},
|
||||
use_hypernetwork_model: {
|
||||
name: "Hypernetwork model",
|
||||
setUI: (use_hypernetwork_model) => {
|
||||
const oldVal = hypernetworkModelField.value
|
||||
use_hypernetwork_model =
|
||||
use_hypernetwork_model === undefined ||
|
||||
use_hypernetwork_model === null ||
|
||||
use_hypernetwork_model === "None"
|
||||
? ""
|
||||
: use_hypernetwork_model
|
||||
|
||||
if (use_hypernetwork_model !== "") {
|
||||
use_hypernetwork_model = getModelPath(use_hypernetwork_model, [".pt"])
|
||||
use_hypernetwork_model = use_hypernetwork_model !== "" ? use_hypernetwork_model : oldVal
|
||||
}
|
||||
hypernetworkModelField.value = use_hypernetwork_model
|
||||
hypernetworkModelField.dispatchEvent(new Event("change"))
|
||||
},
|
||||
readUI: () => hypernetworkModelField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
hypernetwork_strength: {
|
||||
name: "Hypernetwork Strength",
|
||||
setUI: (hypernetwork_strength) => {
|
||||
hypernetworkStrengthField.value = hypernetwork_strength
|
||||
updateHypernetworkStrengthSlider()
|
||||
},
|
||||
readUI: () => parseFloat(hypernetworkStrengthField.value),
|
||||
parse: (val) => parseFloat(val),
|
||||
},
|
||||
|
||||
num_outputs: {
|
||||
name: "Parallel Images",
|
||||
setUI: (num_outputs) => {
|
||||
numOutputsParallelField.value = num_outputs
|
||||
},
|
||||
readUI: () => parseInt(numOutputsParallelField.value),
|
||||
parse: (val) => val,
|
||||
},
|
||||
|
||||
use_cpu: {
|
||||
name: "Use CPU",
|
||||
setUI: (use_cpu) => {
|
||||
useCPUField.checked = use_cpu
|
||||
},
|
||||
readUI: () => useCPUField.checked,
|
||||
parse: (val) => val,
|
||||
},
|
||||
|
||||
stream_image_progress: {
|
||||
name: "Stream Image Progress",
|
||||
setUI: (stream_image_progress) => {
|
||||
streamImageProgressField.checked = parseInt(numOutputsTotalField.value) > 50 ? false : stream_image_progress
|
||||
},
|
||||
readUI: () => streamImageProgressField.checked,
|
||||
parse: (val) => Boolean(val),
|
||||
},
|
||||
show_only_filtered_image: {
|
||||
name: "Show only the corrected/upscaled image",
|
||||
setUI: (show_only_filtered_image) => {
|
||||
showOnlyFilteredImageField.checked = show_only_filtered_image
|
||||
},
|
||||
readUI: () => showOnlyFilteredImageField.checked,
|
||||
parse: (val) => Boolean(val),
|
||||
},
|
||||
output_format: {
|
||||
name: "Output Format",
|
||||
setUI: (output_format) => {
|
||||
outputFormatField.value = output_format
|
||||
},
|
||||
readUI: () => outputFormatField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
save_to_disk_path: {
|
||||
name: "Save to disk path",
|
||||
setUI: (save_to_disk_path) => {
|
||||
saveToDiskField.checked = Boolean(save_to_disk_path)
|
||||
diskPathField.value = save_to_disk_path
|
||||
},
|
||||
readUI: () => diskPathField.value,
|
||||
parse: (val) => val,
|
||||
},
|
||||
}
|
||||
|
||||
function restoreTaskToUI(task, fieldsToSkip) {
|
||||
fieldsToSkip = fieldsToSkip || []
|
||||
|
||||
if ("numOutputsTotal" in task) {
|
||||
numOutputsTotalField.value = task.numOutputsTotal
|
||||
}
|
||||
if ("seed" in task) {
|
||||
randomSeedField.checked = false
|
||||
seedField.value = task.seed
|
||||
}
|
||||
if (!("reqBody" in task)) {
|
||||
return
|
||||
}
|
||||
for (const key in TASK_MAPPING) {
|
||||
if (key in task.reqBody && !fieldsToSkip.includes(key)) {
|
||||
TASK_MAPPING[key].setUI(task.reqBody[key])
|
||||
}
|
||||
}
|
||||
|
||||
// properly reset fields not present in the task
|
||||
if (!("use_hypernetwork_model" in task.reqBody)) {
|
||||
hypernetworkModelField.value = ""
|
||||
hypernetworkModelField.dispatchEvent(new Event("change"))
|
||||
}
|
||||
|
||||
if (!("use_lora_model" in task.reqBody)) {
|
||||
loraModelField.modelNames = []
|
||||
loraModelField.modelWeights = []
|
||||
}
|
||||
|
||||
// restore the original prompt if provided (e.g. use settings), fallback to prompt as needed (e.g. copy/paste or d&d)
|
||||
promptField.value = task.reqBody.original_prompt
|
||||
if (!("original_prompt" in task.reqBody)) {
|
||||
promptField.value = task.reqBody.prompt
|
||||
}
|
||||
promptField.dispatchEvent(new Event("input"))
|
||||
|
||||
// properly reset checkboxes
|
||||
if (!("use_face_correction" in task.reqBody)) {
|
||||
useFaceCorrectionField.checked = false
|
||||
gfpganModelField.disabled = true
|
||||
}
|
||||
if (!("use_upscale" in task.reqBody)) {
|
||||
useUpscalingField.checked = false
|
||||
}
|
||||
if (!("mask" in task.reqBody) && maskSetting.checked) {
|
||||
maskSetting.checked = false
|
||||
maskSetting.dispatchEvent(new Event("click"))
|
||||
}
|
||||
upscaleModelField.disabled = !useUpscalingField.checked
|
||||
upscaleAmountField.disabled = !useUpscalingField.checked
|
||||
|
||||
// hide/show source picture as needed
|
||||
if (IMAGE_REGEX.test(initImagePreview.src) && task.reqBody.init_image == undefined) {
|
||||
// hide source image
|
||||
initImageClearBtn.dispatchEvent(new Event("click"))
|
||||
} else if (task.reqBody.init_image !== undefined) {
|
||||
// listen for inpainter loading event, which happens AFTER the main image loads (which reloads the inpainter)
|
||||
initImagePreview.addEventListener(
|
||||
"load",
|
||||
function () {
|
||||
if (Boolean(task.reqBody.mask)) {
|
||||
imageInpainter.setImg(task.reqBody.mask)
|
||||
maskSetting.checked = true
|
||||
}
|
||||
},
|
||||
{ once: true }
|
||||
)
|
||||
initImagePreview.src = task.reqBody.init_image
|
||||
}
|
||||
|
||||
// hide/show controlnet picture as needed
|
||||
if (IMAGE_REGEX.test(controlImagePreview.src) && task.reqBody.control_image == undefined) {
|
||||
// hide source image
|
||||
controlImageClearBtn.dispatchEvent(new Event("click"))
|
||||
} else if (task.reqBody.control_image !== undefined) {
|
||||
// listen for inpainter loading event, which happens AFTER the main image loads (which reloads the inpai
|
||||
controlImagePreview.src = task.reqBody.control_image
|
||||
}
|
||||
|
||||
// restore ref images
|
||||
if (task.reqBody.ref_images !== undefined && Array.isArray(task.reqBody.ref_images) && task.reqBody.ref_images.length > 0) {
|
||||
refImages = task.reqBody.ref_images.slice()
|
||||
renderRefImagesList()
|
||||
} else if (refImages.length > 0) {
|
||||
clearAllRefImages()
|
||||
}
|
||||
|
||||
if ("use_controlnet_model" in task.reqBody && task.reqBody.use_controlnet_model && !("control_alpha" in task.reqBody)) {
|
||||
controlAlphaField.value = 1.0
|
||||
updateControlAlphaSlider()
|
||||
}
|
||||
}
|
||||
function readUI() {
|
||||
const reqBody = {}
|
||||
for (const key in TASK_MAPPING) {
|
||||
if (testDiffusers.checked && (key === "use_hypernetwork_model" || key === "hypernetwork_strength")) {
|
||||
continue
|
||||
}
|
||||
|
||||
reqBody[key] = TASK_MAPPING[key].readUI()
|
||||
}
|
||||
return {
|
||||
numOutputsTotal: parseInt(numOutputsTotalField.value),
|
||||
seed: TASK_MAPPING["seed"].readUI(),
|
||||
reqBody: reqBody,
|
||||
}
|
||||
}
|
||||
function getModelPath(filename, extensions) {
|
||||
if (typeof filename !== "string") {
|
||||
return
|
||||
}
|
||||
|
||||
let pathIdx
|
||||
if (filename.includes("/models/stable-diffusion/")) {
|
||||
pathIdx = filename.indexOf("/models/stable-diffusion/") + 25 // Linux, Mac paths
|
||||
} else if (filename.includes("\\models\\stable-diffusion\\")) {
|
||||
pathIdx = filename.indexOf("\\models\\stable-diffusion\\") + 25 // Linux, Mac paths
|
||||
}
|
||||
if (pathIdx >= 0) {
|
||||
filename = filename.slice(pathIdx)
|
||||
}
|
||||
extensions.forEach((ext) => {
|
||||
if (filename.endsWith(ext)) {
|
||||
filename = filename.slice(0, filename.length - ext.length)
|
||||
}
|
||||
})
|
||||
return filename
|
||||
}
|
||||
|
||||
const TASK_TEXT_MAPPING = {
|
||||
prompt: "Prompt",
|
||||
width: "Width",
|
||||
height: "Height",
|
||||
seed: "Seed",
|
||||
num_inference_steps: "Steps",
|
||||
guidance_scale: ["Guidance Scale", "CFG Scale"],
|
||||
distilled_guidance_scale: "Distilled Guidance",
|
||||
prompt_strength: ["Prompt Strength", "Denoising Strength"],
|
||||
use_face_correction: "Use Face Correction",
|
||||
use_upscale: "Use Upscaling",
|
||||
upscale_amount: "Upscale By",
|
||||
sampler_name: "Sampler",
|
||||
scheduler_name: "Scheduler",
|
||||
negative_prompt: "Negative Prompt",
|
||||
use_stable_diffusion_model: "Stable Diffusion model",
|
||||
use_hypernetwork_model: "Hypernetwork model",
|
||||
hypernetwork_strength: "Hypernetwork Strength",
|
||||
use_lora_model: "LoRA model",
|
||||
lora_alpha: "LoRA Strength",
|
||||
use_text_encoder_model: "Text Encoder model",
|
||||
use_controlnet_model: "ControlNet model",
|
||||
control_filter_to_apply: "ControlNet Filter",
|
||||
control_alpha: "ControlNet Strength",
|
||||
tiling: "Seamless Tiling",
|
||||
}
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
||||
}
|
||||
function getTaskTextLabels(key) {
|
||||
const val = TASK_TEXT_MAPPING[key]
|
||||
return Array.isArray(val) ? val : [val]
|
||||
}
|
||||
function parseTaskFromText(str) {
|
||||
const taskReqBody = {}
|
||||
|
||||
const lines = str.split("\n")
|
||||
if (lines.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// Prompt
|
||||
let knownKeyOnFirstLine = false
|
||||
for (let key in TASK_TEXT_MAPPING) {
|
||||
for (const name of getTaskTextLabels(key)) {
|
||||
if (lines[0].startsWith(name + ":")) {
|
||||
knownKeyOnFirstLine = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (knownKeyOnFirstLine) break
|
||||
}
|
||||
if (!knownKeyOnFirstLine) {
|
||||
taskReqBody.prompt = lines[0]
|
||||
console.log("Prompt:", taskReqBody.prompt)
|
||||
}
|
||||
|
||||
for (const key in TASK_TEXT_MAPPING) {
|
||||
if (key in taskReqBody) {
|
||||
continue
|
||||
}
|
||||
|
||||
let val = undefined
|
||||
|
||||
for (const name of getTaskTextLabels(key)) {
|
||||
const reName = new RegExp(`${escapeRegExp(name)}\\ *:\\ *(.*)(?:\\r\\n|\\r|\\n)*`, "igm")
|
||||
const match = reName.exec(str)
|
||||
if (match) {
|
||||
str = str.slice(0, match.index) + str.slice(match.index + match[0].length)
|
||||
val = match[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
if (val !== undefined) {
|
||||
taskReqBody[key] = TASK_MAPPING[key].parse(val.trim())
|
||||
console.log(TASK_MAPPING[key].name + ":", taskReqBody[key])
|
||||
if (!str) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(taskReqBody).length <= 0) {
|
||||
return undefined
|
||||
}
|
||||
const task = { reqBody: taskReqBody }
|
||||
if ("seed" in taskReqBody) {
|
||||
task.seed = taskReqBody.seed
|
||||
}
|
||||
return task
|
||||
}
|
||||
|
||||
async function parseContent(text) {
|
||||
text = text.trim()
|
||||
if (text.startsWith("{") && text.endsWith("}")) {
|
||||
try {
|
||||
const task = JSON.parse(text)
|
||||
if (!("reqBody" in task)) {
|
||||
// support the format saved to the disk, by the UI
|
||||
task.reqBody = Object.assign({}, task)
|
||||
}
|
||||
restoreTaskToUI(task)
|
||||
return true
|
||||
} catch (e) {
|
||||
console.warn(`JSON text content couldn't be parsed.`, e)
|
||||
}
|
||||
return false
|
||||
}
|
||||
// Normal txt file.
|
||||
const task = parseTaskFromText(text)
|
||||
if (text.toLowerCase().includes("seed:") && task) {
|
||||
// only parse valid task content
|
||||
restoreTaskToUI(task)
|
||||
return true
|
||||
} else {
|
||||
console.warn(`Raw text content couldn't be parsed.`)
|
||||
promptField.value = text
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function readFile(file, i) {
|
||||
console.log(`Event %o reading file[${i}]:${file.name}...`)
|
||||
const fileContent = (await file.text()).trim()
|
||||
return await parseContent(fileContent)
|
||||
}
|
||||
|
||||
function dropHandler(ev) {
|
||||
console.log("Content dropped...")
|
||||
let items = []
|
||||
|
||||
if (ev?.dataTransfer?.items) {
|
||||
// Use DataTransferItemList interface
|
||||
items = Array.from(ev.dataTransfer.items)
|
||||
items = items.filter((item) => item.kind === "file")
|
||||
items = items.map((item) => item.getAsFile())
|
||||
} else if (ev?.dataTransfer?.files) {
|
||||
// Use DataTransfer interface
|
||||
items = Array.from(ev.dataTransfer.files)
|
||||
}
|
||||
|
||||
items.forEach((item) => {
|
||||
item.file_ext = EXT_REGEX.exec(item.name.toLowerCase())[1]
|
||||
})
|
||||
|
||||
let text_items = items.filter((item) => TEXT_EXTENSIONS.includes(item.file_ext))
|
||||
let image_items = items.filter((item) => IMAGE_EXTENSIONS.includes(item.file_ext))
|
||||
|
||||
if (image_items.length > 0 && ev.target == initImageSelector) {
|
||||
return // let the event bubble up, so that the Init Image filepicker can receive this
|
||||
}
|
||||
|
||||
ev.preventDefault() // Prevent default behavior (Prevent file/content from being opened)
|
||||
text_items.forEach(readFile)
|
||||
}
|
||||
function dragOverHandler(ev) {
|
||||
console.log("Content in drop zone")
|
||||
|
||||
// Prevent default behavior (Prevent file/content from being opened)
|
||||
ev.preventDefault()
|
||||
|
||||
ev.dataTransfer.dropEffect = "copy"
|
||||
|
||||
let img = new Image()
|
||||
img.src = "//" + location.host + "/media/images/favicon-32x32.png"
|
||||
ev.dataTransfer.setDragImage(img, 16, 16)
|
||||
}
|
||||
|
||||
document.addEventListener("drop", dropHandler)
|
||||
document.addEventListener("dragover", dragOverHandler)
|
||||
|
||||
const TASK_REQ_NO_EXPORT = ["use_cpu", "save_to_disk_path"]
|
||||
const resetSettings = document.getElementById("reset-image-settings")
|
||||
|
||||
function checkReadTextClipboardPermission(result) {
|
||||
if (result.state != "granted" && result.state != "prompt") {
|
||||
return
|
||||
}
|
||||
// PASTE ICON
|
||||
const pasteIcon = document.createElement("i")
|
||||
pasteIcon.className = "fa-solid fa-paste section-button"
|
||||
pasteIcon.innerHTML = `<span class="simple-tooltip top-left">Paste Image Settings</span>`
|
||||
pasteIcon.addEventListener("click", async (event) => {
|
||||
event.stopPropagation()
|
||||
// Add css class 'active'
|
||||
pasteIcon.classList.add("active")
|
||||
// In 350 ms remove the 'active' class
|
||||
asyncDelay(350).then(() => pasteIcon.classList.remove("active"))
|
||||
|
||||
// Retrieve clipboard content and try to parse it
|
||||
const text = await navigator.clipboard.readText()
|
||||
await parseContent(text)
|
||||
})
|
||||
resetSettings.parentNode.insertBefore(pasteIcon, resetSettings)
|
||||
}
|
||||
navigator.permissions
|
||||
.query({ name: "clipboard-read" })
|
||||
.then(checkReadTextClipboardPermission, (reason) => console.log("clipboard-read is not available. %o", reason))
|
||||
|
||||
document.addEventListener("paste", async (event) => {
|
||||
if (event.target) {
|
||||
const targetTag = event.target.tagName.toLowerCase()
|
||||
// Disable when targeting input elements.
|
||||
if (targetTag === "input" || targetTag === "textarea") {
|
||||
return
|
||||
}
|
||||
}
|
||||
const paste = (event.clipboardData || window.clipboardData).getData("text")
|
||||
const selection = window.getSelection()
|
||||
if (paste != "" && selection.toString().trim().length <= 0 && (await parseContent(paste))) {
|
||||
event.preventDefault()
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
// Adds a copy and a paste icon if the browser grants permission to write to clipboard.
|
||||
function checkWriteToClipboardPermission(result) {
|
||||
if (result.state != "granted" && result.state != "prompt") {
|
||||
return
|
||||
}
|
||||
// COPY ICON
|
||||
const copyIcon = document.createElement("i")
|
||||
copyIcon.className = "fa-solid fa-clipboard section-button"
|
||||
copyIcon.innerHTML = `<span class="simple-tooltip top-left">Copy Image Settings</span>`
|
||||
copyIcon.addEventListener("click", (event) => {
|
||||
event.stopPropagation()
|
||||
// Add css class 'active'
|
||||
copyIcon.classList.add("active")
|
||||
// In 350 ms remove the 'active' class
|
||||
asyncDelay(350).then(() => copyIcon.classList.remove("active"))
|
||||
const uiState = readUI()
|
||||
TASK_REQ_NO_EXPORT.forEach((key) => delete uiState.reqBody[key])
|
||||
if (uiState.reqBody.init_image && !IMAGE_REGEX.test(uiState.reqBody.init_image)) {
|
||||
delete uiState.reqBody.init_image
|
||||
delete uiState.reqBody.prompt_strength
|
||||
}
|
||||
navigator.clipboard.writeText(JSON.stringify(uiState, undefined, 4))
|
||||
})
|
||||
resetSettings.parentNode.insertBefore(copyIcon, resetSettings)
|
||||
}
|
||||
// Determine which access we have to the clipboard. Clipboard access is only available on localhost or via TLS.
|
||||
navigator.permissions.query({ name: "clipboard-write" }).then(checkWriteToClipboardPermission, (e) => {
|
||||
if (e instanceof TypeError && typeof navigator?.clipboard?.writeText === "function") {
|
||||
// Fix for firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1560373
|
||||
checkWriteToClipboardPermission({ state: "granted" })
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user