easydiffusion/ui/media/js/dnd.js

781 lines
26 KiB
JavaScript
Raw Normal View History

"use strict" // Opt in to a restricted variant of JavaScript
const EXT_REGEX = /(?:\.([^.]+))?$/
2023-04-27 19:56:56 +02:00
const TEXT_EXTENSIONS = ["txt", "json"]
const IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "bmp", "tiff", "tif", "tga", "webp"]
function parseBoolean(stringValue) {
2023-04-27 19:56:56 +02:00
if (typeof stringValue === "boolean") {
return stringValue
}
2023-04-27 19:56:56 +02:00
if (typeof stringValue === "number") {
return stringValue !== 0
}
2023-04-27 19:56:56 +02:00
if (typeof stringValue !== "string") {
return false
}
2023-04-27 19:56:56 +02:00
switch (stringValue?.toLowerCase()?.trim()) {
case "true":
case "yes":
case "on":
case "1":
2023-04-27 19:56:56 +02:00
return true
case "false":
case "no":
case "off":
case "0":
case "none":
case null:
case undefined:
2023-04-27 19:56:56 +02:00
return false
}
try {
2023-04-27 19:56:56 +02:00
return Boolean(JSON.parse(stringValue))
} catch {
return Boolean(stringValue)
}
}
// keep in sync with `ui/easydiffusion/utils/save_utils.py`
const TASK_MAPPING = {
2023-04-27 19:56:56 +02:00
prompt: {
name: "Prompt",
setUI: (prompt) => {
promptField.value = prompt
},
readUI: () => promptField.value,
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
negative_prompt: {
name: "Negative Prompt",
setUI: (negative_prompt) => {
negativePromptField.value = negative_prompt
},
readUI: () => negativePromptField.value,
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
active_tags: {
name: "Image Modifiers",
setUI: (active_tags) => {
refreshModifiersState(active_tags)
},
2023-04-27 19:56:56 +02:00
readUI: () => activeTags.map((x) => x.name),
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
inactive_tags: {
name: "Inactive Image Modifiers",
setUI: (inactive_tags) => {
refreshInactiveTags(inactive_tags)
},
2023-04-27 19:56:56 +02:00
readUI: () => activeTags.filter((tag) => tag.inactive === true).map((x) => x.name),
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
width: {
name: "Width",
setUI: (width) => {
const oldVal = widthField.value
widthField.value = width
if (!widthField.value) {
widthField.value = oldVal
}
widthField.dispatchEvent(new Event("change"))
},
readUI: () => parseInt(widthField.value),
parse: (val) => parseInt(val),
},
2023-04-27 19:56:56 +02:00
height: {
name: "Height",
setUI: (height) => {
const oldVal = heightField.value
heightField.value = height
if (!heightField.value) {
heightField.value = oldVal
}
heightField.dispatchEvent(new Event("change"))
},
readUI: () => parseInt(heightField.value),
parse: (val) => parseInt(val),
},
2023-04-27 19:56:56 +02:00
seed: {
name: "Seed",
setUI: (seed) => {
if (!seed) {
randomSeedField.checked = true
seedField.disabled = true
seedField.value = 0
return
}
randomSeedField.checked = false
2023-04-27 19:56:56 +02:00
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),
},
2023-04-27 19:56:56 +02:00
num_inference_steps: {
name: "Steps",
setUI: (num_inference_steps) => {
numInferenceStepsField.value = num_inference_steps
},
readUI: () => parseInt(numInferenceStepsField.value),
parse: (val) => parseInt(val),
},
2023-04-27 19:56:56 +02:00
guidance_scale: {
name: "Guidance Scale",
setUI: (guidance_scale) => {
guidanceScaleField.value = guidance_scale
2022-11-02 15:53:48 +01:00
updateGuidanceScaleSlider()
},
readUI: () => parseFloat(guidanceScaleField.value),
parse: (val) => parseFloat(val),
},
2023-04-27 19:56:56 +02:00
prompt_strength: {
name: "Prompt Strength",
setUI: (prompt_strength) => {
promptStrengthField.value = prompt_strength
2022-11-02 15:53:48 +01:00
updatePromptStrengthSlider()
},
readUI: () => parseFloat(promptStrengthField.value),
parse: (val) => parseFloat(val),
},
2023-04-27 19:56:56 +02:00
init_image: {
name: "Initial Image",
setUI: (init_image) => {
initImagePreview.src = init_image
},
readUI: () => initImagePreview.src,
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
mask: {
name: "Mask",
setUI: (mask) => {
2023-04-27 19:56:56 +02:00
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,
},
2023-04-27 19:56:56 +02:00
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),
},
2023-04-27 19:56:56 +02:00
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,
},
2023-04-27 19:56:56 +02:00
use_upscale: {
name: "Use Upscaling",
setUI: (use_upscale) => {
const oldVal = upscaleModelField.value
2023-04-27 19:56:56 +02:00
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
2023-04-27 19:56:56 +02:00
} 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,
},
2023-04-27 19:56:56 +02:00
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,
},
2023-04-27 19:56:56 +02:00
sampler_name: {
name: "Sampler",
2022-12-14 05:51:44 +01:00
setUI: (sampler_name) => {
samplerField.value = sampler_name
},
readUI: () => samplerField.value,
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
use_stable_diffusion_model: {
name: "Stable Diffusion model",
setUI: (use_stable_diffusion_model) => {
const oldVal = stableDiffusionModelField.value
2022-10-31 04:41:26 +01:00
2023-04-27 19:56:56 +02:00
use_stable_diffusion_model = getModelPath(use_stable_diffusion_model, [".ckpt", ".safetensors"])
stableDiffusionModelField.value = use_stable_diffusion_model
2022-10-31 04:41:26 +01:00
if (!stableDiffusionModelField.value) {
stableDiffusionModelField.value = oldVal
}
},
readUI: () => stableDiffusionModelField.value,
parse: (val) => val,
},
2023-05-18 13:55:45 +02:00
clip_skip: {
name: "Clip Skip",
setUI: (value) => {
clip_skip.checked = value
},
readUI: () => clip_skip.checked,
parse: (val) => Boolean(val),
},
2023-05-25 00:16:14 +02:00
tiling: {
name: "Tiling",
setUI: (val) => {
tilingField.value = val
},
readUI: () => tilingField.value,
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
use_vae_model: {
name: "VAE model",
setUI: (use_vae_model) => {
const oldVal = vaeModelField.value
2023-04-27 19:56:56 +02:00
use_vae_model =
use_vae_model === undefined || use_vae_model === null || use_vae_model === "None" ? "" : use_vae_model
2023-04-27 19:56:56 +02:00
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,
},
2023-04-27 19:56:56 +02:00
use_lora_model: {
name: "LoRA model",
setUI: (use_lora_model) => {
// create rows
for (let i = loraModels.length; i < use_lora_model.length; i++) {
createLoraEntry()
}
2023-07-15 16:03:49 +02:00
use_lora_model.forEach((model_name, i) => {
let field = loraModels[i][0]
const oldVal = field.value
if (model_name !== "") {
model_name = getModelPath(model_name, [".ckpt", ".safetensors"])
model_name = model_name !== "" ? model_name : oldVal
}
field.value = model_name
})
2023-04-27 19:56:56 +02:00
2023-07-15 16:03:49 +02:00
// clear the remaining entries
let container = document.querySelector("#lora_model_container .model_entries")
2023-07-15 16:03:49 +02:00
for (let i = use_lora_model.length; i < loraModels.length; i++) {
let modelEntry = loraModels[i][2]
container.removeChild(modelEntry)
}
loraModels.splice(use_lora_model.length)
},
2023-07-15 16:03:49 +02:00
readUI: () => {
let values = loraModels.map((e) => e[0].value)
values = values.filter((e) => e.trim() !== "")
values = values.length > 0 ? values : "None"
return values
},
parse: (val) => {
val = !val || val === "None" ? "" : val
val = Array.isArray(val) ? val : [val]
return val
},
},
2023-04-27 19:56:56 +02:00
lora_alpha: {
name: "LoRA Strength",
2023-04-01 17:42:20 +02:00
setUI: (lora_alpha) => {
for (let i = loraModels.length; i < lora_alpha.length; i++) {
createLoraEntry()
}
2023-07-15 16:03:49 +02:00
lora_alpha.forEach((model_strength, i) => {
let field = loraModels[i][1]
field.value = model_strength
})
// clear the remaining entries
let container = document.querySelector("#lora_model_container .model_entries")
2023-07-15 16:03:49 +02:00
for (let i = lora_alpha.length; i < loraModels.length; i++) {
let modelEntry = loraModels[i][2]
container.removeChild(modelEntry)
2023-07-15 16:03:49 +02:00
}
loraModels.splice(lora_alpha.length)
2023-07-15 16:03:49 +02:00
},
readUI: () => {
let models = loraModels.filter((e) => e[0].value.trim() !== "")
let values = models.map((e) => e[1].value)
values = values.length > 0 ? values : 0
return values
},
parse: (val) => {
val = Array.isArray(val) ? val : [val]
val = val.map((e) => parseFloat(e))
return val
2023-04-01 17:42:20 +02:00
},
},
2023-04-27 19:56:56 +02:00
use_hypernetwork_model: {
name: "Hypernetwork model",
setUI: (use_hypernetwork_model) => {
const oldVal = hypernetworkModelField.value
2023-04-27 19:56:56 +02:00
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
2023-04-27 19:56:56 +02:00
hypernetworkModelField.dispatchEvent(new Event("change"))
},
readUI: () => hypernetworkModelField.value,
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
hypernetwork_strength: {
name: "Hypernetwork Strength",
setUI: (hypernetwork_strength) => {
hypernetworkStrengthField.value = hypernetwork_strength
updateHypernetworkStrengthSlider()
},
readUI: () => parseFloat(hypernetworkStrengthField.value),
parse: (val) => parseFloat(val),
},
2023-04-27 19:56:56 +02:00
num_outputs: {
name: "Parallel Images",
setUI: (num_outputs) => {
numOutputsParallelField.value = num_outputs
},
readUI: () => parseInt(numOutputsParallelField.value),
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
use_cpu: {
name: "Use CPU",
setUI: (use_cpu) => {
useCPUField.checked = use_cpu
},
readUI: () => useCPUField.checked,
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
stream_image_progress: {
name: "Stream Image Progress",
setUI: (stream_image_progress) => {
2023-04-27 19:56:56 +02:00
streamImageProgressField.checked = parseInt(numOutputsTotalField.value) > 50 ? false : stream_image_progress
},
readUI: () => streamImageProgressField.checked,
parse: (val) => Boolean(val),
},
2023-04-27 19:56:56 +02:00
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),
},
2023-04-27 19:56:56 +02:00
output_format: {
name: "Output Format",
setUI: (output_format) => {
outputFormatField.value = output_format
},
readUI: () => outputFormatField.value,
parse: (val) => val,
},
2023-04-27 19:56:56 +02:00
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 || []
2023-04-27 19:56:56 +02:00
if ("numOutputsTotal" in task) {
numOutputsTotalField.value = task.numOutputsTotal
}
2023-04-27 19:56:56 +02:00
if ("seed" in task) {
randomSeedField.checked = false
seedField.value = task.seed
}
2023-04-27 19:56:56 +02:00
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
2023-04-27 19:56:56 +02:00
if (!("use_hypernetwork_model" in task.reqBody)) {
hypernetworkModelField.value = ""
hypernetworkModelField.dispatchEvent(new Event("change"))
}
2023-04-27 19:56:56 +02:00
if (!("use_lora_model" in task.reqBody)) {
2023-07-15 16:03:49 +02:00
loraModels.forEach((e) => {
e[0].value = ""
e[1].value = 0
e[0].dispatchEvent(new Event("change"))
})
}
2023-04-27 19:56:56 +02:00
// 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
2023-04-27 19:56:56 +02:00
if (!("original_prompt" in task.reqBody)) {
promptField.value = task.reqBody.prompt
}
promptField.dispatchEvent(new Event("input"))
2023-04-27 19:56:56 +02:00
// properly reset checkboxes
2023-04-27 19:56:56 +02:00
if (!("use_face_correction" in task.reqBody)) {
useFaceCorrectionField.checked = false
gfpganModelField.disabled = true
}
2023-04-27 19:56:56 +02:00
if (!("use_upscale" in task.reqBody)) {
useUpscalingField.checked = false
}
2023-04-27 19:56:56 +02:00
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
2022-12-29 02:00:38 +01:00
initImageClearBtn.dispatchEvent(new Event("click"))
2023-04-27 19:56:56 +02:00
} else if (task.reqBody.init_image !== undefined) {
// listen for inpainter loading event, which happens AFTER the main image loads (which reloads the inpainter)
2023-04-27 19:56:56 +02:00
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
}
}
2022-10-31 06:13:04 +01:00
function readUI() {
2022-10-31 06:02:23 +01:00
const reqBody = {}
for (const key in TASK_MAPPING) {
reqBody[key] = TASK_MAPPING[key].readUI()
}
return {
2023-04-27 19:56:56 +02:00
numOutputsTotal: parseInt(numOutputsTotalField.value),
seed: TASK_MAPPING["seed"].readUI(),
reqBody: reqBody,
2022-10-31 06:02:23 +01:00
}
}
2023-04-27 19:56:56 +02:00
function getModelPath(filename, extensions) {
if (typeof filename !== "string") {
return
}
2023-04-27 19:56:56 +02:00
2023-02-07 07:19:57 +01:00
let pathIdx
2023-04-27 19:56:56 +02:00
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) {
2023-02-07 07:19:57 +01:00
filename = filename.slice(pathIdx)
}
2023-04-27 19:56:56 +02:00
extensions.forEach((ext) => {
if (filename.endsWith(ext)) {
filename = filename.slice(0, filename.length - ext.length)
}
})
2022-11-24 22:47:35 +01:00
return filename
}
2022-10-31 06:02:23 +01:00
const TASK_TEXT_MAPPING = {
2023-04-27 19:56:56 +02:00
prompt: "Prompt",
width: "Width",
height: "Height",
seed: "Seed",
num_inference_steps: "Steps",
guidance_scale: "Guidance Scale",
prompt_strength: "Prompt Strength",
use_face_correction: "Use Face Correction",
use_upscale: "Use Upscaling",
upscale_amount: "Upscale By",
sampler_name: "Sampler",
negative_prompt: "Negative Prompt",
use_stable_diffusion_model: "Stable Diffusion model",
use_hypernetwork_model: "Hypernetwork model",
hypernetwork_strength: "Hypernetwork Strength",
}
function parseTaskFromText(str) {
const taskReqBody = {}
2023-04-27 19:56:56 +02:00
const lines = str.split("\n")
if (lines.length === 0) {
return
}
// Prompt
let knownKeyOnFirstLine = false
for (let key in TASK_TEXT_MAPPING) {
2023-04-27 19:56:56 +02:00
if (lines[0].startsWith(TASK_TEXT_MAPPING[key] + ":")) {
knownKeyOnFirstLine = true
break
}
}
if (!knownKeyOnFirstLine) {
taskReqBody.prompt = lines[0]
2023-04-27 19:56:56 +02:00
console.log("Prompt:", taskReqBody.prompt)
}
for (const key in TASK_TEXT_MAPPING) {
if (key in taskReqBody) {
continue
}
2023-04-27 19:56:56 +02:00
const name = TASK_TEXT_MAPPING[key]
let val = undefined
2023-04-27 19:56:56 +02:00
const reName = new RegExp(`${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]
}
if (val !== undefined) {
taskReqBody[key] = TASK_MAPPING[key].parse(val.trim())
2023-04-27 19:56:56 +02:00
console.log(TASK_MAPPING[key].name + ":", taskReqBody[key])
if (!str) {
2022-10-31 06:01:56 +01:00
break
}
}
}
if (Object.keys(taskReqBody).length <= 0) {
return undefined
}
const task = { reqBody: taskReqBody }
2023-04-27 19:56:56 +02:00
if ("seed" in taskReqBody) {
task.seed = taskReqBody.seed
}
return task
}
async function parseContent(text) {
2023-04-27 19:56:56 +02:00
text = text.trim()
if (text.startsWith("{") && text.endsWith("}")) {
try {
const task = JSON.parse(text)
2023-04-27 19:56:56 +02:00
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)
2023-04-27 19:56:56 +02:00
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) {
2022-11-22 17:09:47 +01:00
console.log(`Event %o reading file[${i}]:${file.name}...`)
const fileContent = (await file.text()).trim()
return await parseContent(fileContent)
}
function dropHandler(ev) {
2023-04-27 19:56:56 +02:00
console.log("Content dropped...")
let items = []
2023-04-27 19:56:56 +02:00
if (ev?.dataTransfer?.items) {
// Use DataTransferItemList interface
items = Array.from(ev.dataTransfer.items)
2023-04-27 19:56:56 +02:00
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)
}
2023-04-27 19:56:56 +02:00
items.forEach((item) => {
item.file_ext = EXT_REGEX.exec(item.name.toLowerCase())[1]
})
2023-04-27 19:56:56 +02:00
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) {
2023-04-27 19:56:56 +02:00
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()
2023-04-27 19:56:56 +02:00
img.src = "//" + location.host + "/media/images/favicon-32x32.png"
ev.dataTransfer.setDragImage(img, 16, 16)
}
document.addEventListener("drop", dropHandler)
document.addEventListener("dragover", dragOverHandler)
2022-10-31 06:02:23 +01:00
2023-04-27 19:56:56 +02:00
const TASK_REQ_NO_EXPORT = ["use_cpu", "save_to_disk_path"]
const resetSettings = document.getElementById("reset-image-settings")
2023-04-27 19:56:56 +02:00
function checkReadTextClipboardPermission(result) {
if (result.state != "granted" && result.state != "prompt") {
2022-11-14 01:23:04 +01:00
return
}
// PASTE ICON
2023-04-27 19:56:56 +02:00
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>`
2023-04-27 19:56:56 +02:00
pasteIcon.addEventListener("click", async (event) => {
event.stopPropagation()
// Add css class 'active'
2023-04-27 19:56:56 +02:00
pasteIcon.classList.add("active")
// In 350 ms remove the 'active' class
2023-04-27 19:56:56 +02:00
asyncDelay(350).then(() => pasteIcon.classList.remove("active"))
// Retrieve clipboard content and try to parse it
2023-04-27 19:56:56 +02:00
const text = await navigator.clipboard.readText()
await parseContent(text)
})
resetSettings.parentNode.insertBefore(pasteIcon, resetSettings)
2022-11-14 01:23:04 +01:00
}
2023-04-27 19:56:56 +02:00
navigator.permissions
.query({ name: "clipboard-read" })
.then(checkReadTextClipboardPermission, (reason) => console.log("clipboard-read is not available. %o", reason))
2022-11-14 01:23:04 +01:00
2023-04-27 19:56:56 +02:00
document.addEventListener("paste", async (event) => {
2022-11-17 08:36:14 +01:00
if (event.target) {
const targetTag = event.target.tagName.toLowerCase()
// Disable when targeting input elements.
2023-04-27 19:56:56 +02:00
if (targetTag === "input" || targetTag === "textarea") {
2022-11-17 08:36:14 +01:00
return
}
}
2023-04-27 19:56:56 +02:00
const paste = (event.clipboardData || window.clipboardData).getData("text")
const selection = window.getSelection()
2023-04-27 19:56:56 +02:00
if (paste != "" && selection.toString().trim().length <= 0 && (await parseContent(paste))) {
2022-11-17 08:36:14 +01:00
event.preventDefault()
return
}
2022-11-17 08:36:14 +01:00
})
2022-11-14 01:23:04 +01:00
// Adds a copy and a paste icon if the browser grants permission to write to clipboard.
2023-04-27 19:56:56 +02:00
function checkWriteToClipboardPermission(result) {
if (result.state != "granted" && result.state != "prompt") {
return
2022-10-31 06:02:23 +01:00
}
// COPY ICON
2023-04-27 19:56:56 +02:00
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>`
2023-04-27 19:56:56 +02:00
copyIcon.addEventListener("click", (event) => {
event.stopPropagation()
// Add css class 'active'
2023-04-27 19:56:56 +02:00
copyIcon.classList.add("active")
// In 350 ms remove the 'active' class
2023-04-27 19:56:56 +02:00
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)
2022-10-31 06:02:23 +01:00
}
// Determine which access we have to the clipboard. Clipboard access is only available on localhost or via TLS.
2022-10-31 06:02:23 +01:00
navigator.permissions.query({ name: "clipboard-write" }).then(checkWriteToClipboardPermission, (e) => {
2023-04-27 19:56:56 +02:00
if (e instanceof TypeError && typeof navigator?.clipboard?.writeText === "function") {
2022-10-31 06:02:23 +01:00
// Fix for firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1560373
2023-04-27 19:56:56 +02:00
checkWriteToClipboardPermission({ state: "granted" })
2022-10-31 06:02:23 +01:00
}
})