mirror of
https://github.com/easydiffusion/easydiffusion.git
synced 2024-11-25 01:34:38 +01:00
Merge pull request #1219 from lucasmarcelli/prettier-beta-take-two
add prettier for JS style
This commit is contained in:
commit
af0058d2aa
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ installer
|
|||||||
installer.tar
|
installer.tar
|
||||||
dist
|
dist
|
||||||
.idea/*
|
.idea/*
|
||||||
|
node_modules/*
|
8
.prettierignore
Normal file
8
.prettierignore
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
*.min.*
|
||||||
|
*.py
|
||||||
|
/*
|
||||||
|
!/ui
|
||||||
|
/ui/easydiffusion
|
||||||
|
/ui/hotfix
|
||||||
|
!/ui/plugins
|
||||||
|
!/ui/media
|
7
.prettierrc.json
Normal file
7
.prettierrc.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 120,
|
||||||
|
"tabWidth": 4,
|
||||||
|
"semi": false,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"trailingComma": "none"
|
||||||
|
}
|
8
package.json
Normal file
8
package.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"prettier": "prettier --write \"./**/*.js\""
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^1.19.1"
|
||||||
|
}
|
||||||
|
}
|
@ -55,11 +55,10 @@ const SETTINGS_IDS_LIST = [
|
|||||||
"json_toggle"
|
"json_toggle"
|
||||||
]
|
]
|
||||||
|
|
||||||
const IGNORE_BY_DEFAULT = [
|
const IGNORE_BY_DEFAULT = ["prompt"]
|
||||||
"prompt"
|
|
||||||
]
|
|
||||||
|
|
||||||
const SETTINGS_SECTIONS = [ // gets the "keys" property filled in with an ordered list of settings in this section via initSettings
|
const SETTINGS_SECTIONS = [
|
||||||
|
// gets the "keys" property filled in with an ordered list of settings in this section via initSettings
|
||||||
{ id: "editor-inputs", name: "Prompt" },
|
{ id: "editor-inputs", name: "Prompt" },
|
||||||
{ id: "editor-settings", name: "Image Settings" },
|
{ id: "editor-settings", name: "Image Settings" },
|
||||||
{ id: "system-settings", name: "System Settings" },
|
{ id: "system-settings", name: "System Settings" },
|
||||||
@ -67,12 +66,13 @@ const SETTINGS_SECTIONS = [ // gets the "keys" property filled in with an ordere
|
|||||||
]
|
]
|
||||||
|
|
||||||
async function initSettings() {
|
async function initSettings() {
|
||||||
SETTINGS_IDS_LIST.forEach(id => {
|
SETTINGS_IDS_LIST.forEach((id) => {
|
||||||
var element = document.getElementById(id)
|
var element = document.getElementById(id)
|
||||||
if (!element) {
|
if (!element) {
|
||||||
console.error(`Missing settings element ${id}`)
|
console.error(`Missing settings element ${id}`)
|
||||||
}
|
}
|
||||||
if (id in SETTINGS) { // don't create it again
|
if (id in SETTINGS) {
|
||||||
|
// don't create it again
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SETTINGS[id] = {
|
SETTINGS[id] = {
|
||||||
@ -87,22 +87,22 @@ async function initSettings() {
|
|||||||
element.addEventListener("change", settingChangeHandler)
|
element.addEventListener("change", settingChangeHandler)
|
||||||
})
|
})
|
||||||
var unsorted_settings_ids = [...SETTINGS_IDS_LIST]
|
var unsorted_settings_ids = [...SETTINGS_IDS_LIST]
|
||||||
SETTINGS_SECTIONS.forEach(section => {
|
SETTINGS_SECTIONS.forEach((section) => {
|
||||||
var name = section.name
|
var name = section.name
|
||||||
var element = document.getElementById(section.id)
|
var element = document.getElementById(section.id)
|
||||||
var unsorted_ids = unsorted_settings_ids.map(id => `#${id}`).join(",")
|
var unsorted_ids = unsorted_settings_ids.map((id) => `#${id}`).join(",")
|
||||||
var children = unsorted_ids == "" ? [] : Array.from(element.querySelectorAll(unsorted_ids));
|
var children = unsorted_ids == "" ? [] : Array.from(element.querySelectorAll(unsorted_ids))
|
||||||
section.keys = []
|
section.keys = []
|
||||||
children.forEach(e => {
|
children.forEach((e) => {
|
||||||
section.keys.push(e.id)
|
section.keys.push(e.id)
|
||||||
})
|
})
|
||||||
unsorted_settings_ids = unsorted_settings_ids.filter(id => children.find(e => e.id == id) == undefined)
|
unsorted_settings_ids = unsorted_settings_ids.filter((id) => children.find((e) => e.id == id) == undefined)
|
||||||
})
|
})
|
||||||
loadSettings()
|
loadSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSetting(element) {
|
function getSetting(element) {
|
||||||
if (element.dataset && 'path' in element.dataset) {
|
if (element.dataset && "path" in element.dataset) {
|
||||||
return element.dataset.path
|
return element.dataset.path
|
||||||
}
|
}
|
||||||
if (typeof element === "string" || element instanceof String) {
|
if (typeof element === "string" || element instanceof String) {
|
||||||
@ -114,7 +114,7 @@ function getSetting(element) {
|
|||||||
return element.value
|
return element.value
|
||||||
}
|
}
|
||||||
function setSetting(element, value) {
|
function setSetting(element, value) {
|
||||||
if (element.dataset && 'path' in element.dataset) {
|
if (element.dataset && "path" in element.dataset) {
|
||||||
element.dataset.path = value
|
element.dataset.path = value
|
||||||
return // no need to dispatch any event here because the models are not loaded yet
|
return // no need to dispatch any event here because the models are not loaded yet
|
||||||
}
|
}
|
||||||
@ -127,8 +127,7 @@ function setSetting(element, value) {
|
|||||||
}
|
}
|
||||||
if (element.type == "checkbox") {
|
if (element.type == "checkbox") {
|
||||||
element.checked = value
|
element.checked = value
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
element.value = value
|
element.value = value
|
||||||
}
|
}
|
||||||
element.dispatchEvent(new Event("input"))
|
element.dispatchEvent(new Event("input"))
|
||||||
@ -136,7 +135,7 @@ function setSetting(element, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
var saved_settings = Object.values(SETTINGS).map(setting => {
|
var saved_settings = Object.values(SETTINGS).map((setting) => {
|
||||||
return {
|
return {
|
||||||
key: setting.key,
|
key: setting.key,
|
||||||
value: setting.value,
|
value: setting.value,
|
||||||
@ -151,16 +150,16 @@ function loadSettings() {
|
|||||||
var saved_settings_text = localStorage.getItem(SETTINGS_KEY)
|
var saved_settings_text = localStorage.getItem(SETTINGS_KEY)
|
||||||
if (saved_settings_text) {
|
if (saved_settings_text) {
|
||||||
var saved_settings = JSON.parse(saved_settings_text)
|
var saved_settings = JSON.parse(saved_settings_text)
|
||||||
if (saved_settings.find(s => s.key == "auto_save_settings")?.value == false) {
|
if (saved_settings.find((s) => s.key == "auto_save_settings")?.value == false) {
|
||||||
setSetting("auto_save_settings", false)
|
setSetting("auto_save_settings", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
CURRENTLY_LOADING_SETTINGS = true
|
CURRENTLY_LOADING_SETTINGS = true
|
||||||
saved_settings.forEach(saved_setting => {
|
saved_settings.forEach((saved_setting) => {
|
||||||
var setting = SETTINGS[saved_setting.key]
|
var setting = SETTINGS[saved_setting.key]
|
||||||
if (!setting) {
|
if (!setting) {
|
||||||
console.warn(`Attempted to load setting ${saved_setting.key}, but no setting found`);
|
console.warn(`Attempted to load setting ${saved_setting.key}, but no setting found`)
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
setting.ignore = saved_setting.ignore
|
setting.ignore = saved_setting.ignore
|
||||||
if (!setting.ignore) {
|
if (!setting.ignore) {
|
||||||
@ -169,10 +168,9 @@ function loadSettings() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
CURRENTLY_LOADING_SETTINGS = false
|
CURRENTLY_LOADING_SETTINGS = false
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
CURRENTLY_LOADING_SETTINGS = true
|
CURRENTLY_LOADING_SETTINGS = true
|
||||||
tryLoadOldSettings();
|
tryLoadOldSettings()
|
||||||
CURRENTLY_LOADING_SETTINGS = false
|
CURRENTLY_LOADING_SETTINGS = false
|
||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
@ -180,9 +178,9 @@ function loadSettings() {
|
|||||||
|
|
||||||
function loadDefaultSettingsSection(section_id) {
|
function loadDefaultSettingsSection(section_id) {
|
||||||
CURRENTLY_LOADING_SETTINGS = true
|
CURRENTLY_LOADING_SETTINGS = true
|
||||||
var section = SETTINGS_SECTIONS.find(s => s.id == section_id);
|
var section = SETTINGS_SECTIONS.find((s) => s.id == section_id)
|
||||||
section.keys.forEach(key => {
|
section.keys.forEach((key) => {
|
||||||
var setting = SETTINGS[key];
|
var setting = SETTINGS[key]
|
||||||
setting.value = setting.default
|
setting.value = setting.default
|
||||||
setSetting(setting.element, setting.value)
|
setSetting(setting.element, setting.value)
|
||||||
})
|
})
|
||||||
@ -218,10 +216,10 @@ function getSettingLabel(element) {
|
|||||||
|
|
||||||
function fillSaveSettingsConfigTable() {
|
function fillSaveSettingsConfigTable() {
|
||||||
saveSettingsConfigTable.textContent = ""
|
saveSettingsConfigTable.textContent = ""
|
||||||
SETTINGS_SECTIONS.forEach(section => {
|
SETTINGS_SECTIONS.forEach((section) => {
|
||||||
var section_row = `<tr><th>${section.name}</th><td></td></tr>`
|
var section_row = `<tr><th>${section.name}</th><td></td></tr>`
|
||||||
saveSettingsConfigTable.insertAdjacentHTML("beforeend", section_row)
|
saveSettingsConfigTable.insertAdjacentHTML("beforeend", section_row)
|
||||||
section.keys.forEach(key => {
|
section.keys.forEach((key) => {
|
||||||
var setting = SETTINGS[key]
|
var setting = SETTINGS[key]
|
||||||
var element = setting.element
|
var element = setting.element
|
||||||
var checkbox_id = `shouldsave_${element.id}`
|
var checkbox_id = `shouldsave_${element.id}`
|
||||||
@ -234,7 +232,7 @@ function fillSaveSettingsConfigTable() {
|
|||||||
var newrow = `<tr><td><label for="${checkbox_id}">${setting.label}</label></td><td><input id="${checkbox_id}" name="${checkbox_id}" ${is_checked} type="checkbox" ></td><td><small>(${value})</small></td></tr>`
|
var newrow = `<tr><td><label for="${checkbox_id}">${setting.label}</label></td><td><input id="${checkbox_id}" name="${checkbox_id}" ${is_checked} type="checkbox" ></td><td><small>(${value})</small></td></tr>`
|
||||||
saveSettingsConfigTable.insertAdjacentHTML("beforeend", newrow)
|
saveSettingsConfigTable.insertAdjacentHTML("beforeend", newrow)
|
||||||
var checkbox = document.getElementById(checkbox_id)
|
var checkbox = document.getElementById(checkbox_id)
|
||||||
checkbox.addEventListener("input", event => {
|
checkbox.addEventListener("input", (event) => {
|
||||||
setting.ignore = !checkbox.checked
|
setting.ignore = !checkbox.checked
|
||||||
saveSettings()
|
saveSettings()
|
||||||
})
|
})
|
||||||
@ -245,9 +243,6 @@ function fillSaveSettingsConfigTable() {
|
|||||||
|
|
||||||
// configureSettingsSaveBtn
|
// configureSettingsSaveBtn
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var autoSaveSettings = document.getElementById("auto_save_settings")
|
var autoSaveSettings = document.getElementById("auto_save_settings")
|
||||||
var configSettingsButton = document.createElement("button")
|
var configSettingsButton = document.createElement("button")
|
||||||
configSettingsButton.textContent = "Configure"
|
configSettingsButton.textContent = "Configure"
|
||||||
@ -256,33 +251,32 @@ autoSaveSettings.insertAdjacentElement("beforebegin", configSettingsButton)
|
|||||||
autoSaveSettings.addEventListener("change", () => {
|
autoSaveSettings.addEventListener("change", () => {
|
||||||
configSettingsButton.style.display = autoSaveSettings.checked ? "block" : "none"
|
configSettingsButton.style.display = autoSaveSettings.checked ? "block" : "none"
|
||||||
})
|
})
|
||||||
configSettingsButton.addEventListener('click', () => {
|
configSettingsButton.addEventListener("click", () => {
|
||||||
fillSaveSettingsConfigTable()
|
fillSaveSettingsConfigTable()
|
||||||
saveSettingsConfigOverlay.classList.add("active")
|
saveSettingsConfigOverlay.classList.add("active")
|
||||||
})
|
})
|
||||||
resetImageSettingsButton.addEventListener('click', event => {
|
resetImageSettingsButton.addEventListener("click", (event) => {
|
||||||
loadDefaultSettingsSection("editor-settings");
|
loadDefaultSettingsSection("editor-settings")
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
function tryLoadOldSettings() {
|
function tryLoadOldSettings() {
|
||||||
console.log("Loading old user settings")
|
console.log("Loading old user settings")
|
||||||
// load v1 auto-save.js settings
|
// load v1 auto-save.js settings
|
||||||
var old_map = {
|
var old_map = {
|
||||||
"guidance_scale_slider": "guidance_scale",
|
guidance_scale_slider: "guidance_scale",
|
||||||
"prompt_strength_slider": "prompt_strength"
|
prompt_strength_slider: "prompt_strength"
|
||||||
}
|
}
|
||||||
var settings_key_v1 = "user_settings"
|
var settings_key_v1 = "user_settings"
|
||||||
var saved_settings_text = localStorage.getItem(settings_key_v1)
|
var saved_settings_text = localStorage.getItem(settings_key_v1)
|
||||||
if (saved_settings_text) {
|
if (saved_settings_text) {
|
||||||
var saved_settings = JSON.parse(saved_settings_text)
|
var saved_settings = JSON.parse(saved_settings_text)
|
||||||
Object.keys(saved_settings.should_save).forEach(key => {
|
Object.keys(saved_settings.should_save).forEach((key) => {
|
||||||
key = key in old_map ? old_map[key] : key
|
key = key in old_map ? old_map[key] : key
|
||||||
if (!(key in SETTINGS)) return
|
if (!(key in SETTINGS)) return
|
||||||
SETTINGS[key].ignore = !saved_settings.should_save[key]
|
SETTINGS[key].ignore = !saved_settings.should_save[key]
|
||||||
});
|
})
|
||||||
Object.keys(saved_settings.values).forEach(key => {
|
Object.keys(saved_settings.values).forEach((key) => {
|
||||||
key = key in old_map ? old_map[key] : key
|
key = key in old_map ? old_map[key] : key
|
||||||
if (!(key in SETTINGS)) return
|
if (!(key in SETTINGS)) return
|
||||||
var setting = SETTINGS[key]
|
var setting = SETTINGS[key]
|
||||||
@ -290,38 +284,42 @@ function tryLoadOldSettings() {
|
|||||||
setting.value = saved_settings.values[key]
|
setting.value = saved_settings.values[key]
|
||||||
setSetting(setting.element, setting.value)
|
setSetting(setting.element, setting.value)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
localStorage.removeItem(settings_key_v1)
|
localStorage.removeItem(settings_key_v1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// load old individually stored items
|
// load old individually stored items
|
||||||
var individual_settings_map = { // maps old localStorage-key to new SETTINGS-key
|
var individual_settings_map = {
|
||||||
"soundEnabled": "sound_toggle",
|
// maps old localStorage-key to new SETTINGS-key
|
||||||
"saveToDisk": "save_to_disk",
|
soundEnabled: "sound_toggle",
|
||||||
"useCPU": "use_cpu",
|
saveToDisk: "save_to_disk",
|
||||||
"diskPath": "diskPath",
|
useCPU: "use_cpu",
|
||||||
"useFaceCorrection": "use_face_correction",
|
diskPath: "diskPath",
|
||||||
"useUpscaling": "use_upscale",
|
useFaceCorrection: "use_face_correction",
|
||||||
"showOnlyFilteredImage": "show_only_filtered_image",
|
useUpscaling: "use_upscale",
|
||||||
"streamImageProgress": "stream_image_progress",
|
showOnlyFilteredImage: "show_only_filtered_image",
|
||||||
"outputFormat": "output_format",
|
streamImageProgress: "stream_image_progress",
|
||||||
"autoSaveSettings": "auto_save_settings",
|
outputFormat: "output_format",
|
||||||
};
|
autoSaveSettings: "auto_save_settings"
|
||||||
Object.keys(individual_settings_map).forEach(localStorageKey => {
|
}
|
||||||
var localStorageValue = localStorage.getItem(localStorageKey);
|
Object.keys(individual_settings_map).forEach((localStorageKey) => {
|
||||||
|
var localStorageValue = localStorage.getItem(localStorageKey)
|
||||||
if (localStorageValue !== null) {
|
if (localStorageValue !== null) {
|
||||||
let key = individual_settings_map[localStorageKey]
|
let key = individual_settings_map[localStorageKey]
|
||||||
var setting = SETTINGS[key]
|
var setting = SETTINGS[key]
|
||||||
if (!setting) {
|
if (!setting) {
|
||||||
console.warn(`Attempted to map old setting ${key}, but no setting found`);
|
console.warn(`Attempted to map old setting ${key}, but no setting found`)
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
if (setting.element.type == "checkbox" && (typeof localStorageValue === "string" || localStorageValue instanceof String)) {
|
if (
|
||||||
|
setting.element.type == "checkbox" &&
|
||||||
|
(typeof localStorageValue === "string" || localStorageValue instanceof String)
|
||||||
|
) {
|
||||||
localStorageValue = localStorageValue == "true"
|
localStorageValue = localStorageValue == "true"
|
||||||
}
|
}
|
||||||
setting.value = localStorageValue
|
setting.value = localStorageValue
|
||||||
setSetting(setting.element, setting.value)
|
setSetting(setting.element, setting.value)
|
||||||
localStorage.removeItem(localStorageKey);
|
localStorage.removeItem(localStorageKey)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
"use strict" // Opt in to a restricted variant of JavaScript
|
"use strict" // Opt in to a restricted variant of JavaScript
|
||||||
|
|
||||||
const EXT_REGEX = /(?:\.([^.]+))?$/
|
const EXT_REGEX = /(?:\.([^.]+))?$/
|
||||||
const TEXT_EXTENSIONS = ['txt', 'json']
|
const TEXT_EXTENSIONS = ["txt", "json"]
|
||||||
const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'bmp', 'tiff', 'tif', 'tga', 'webp']
|
const IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "bmp", "tiff", "tif", "tga", "webp"]
|
||||||
|
|
||||||
function parseBoolean(stringValue) {
|
function parseBoolean(stringValue) {
|
||||||
if (typeof stringValue === 'boolean') {
|
if (typeof stringValue === "boolean") {
|
||||||
return stringValue
|
return stringValue
|
||||||
}
|
}
|
||||||
if (typeof stringValue === 'number') {
|
if (typeof stringValue === "number") {
|
||||||
return stringValue !== 0
|
return stringValue !== 0
|
||||||
}
|
}
|
||||||
if (typeof stringValue !== 'string') {
|
if (typeof stringValue !== "string") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
switch (stringValue?.toLowerCase()?.trim()) {
|
switch (stringValue?.toLowerCase()?.trim()) {
|
||||||
@ -19,7 +19,7 @@ function parseBoolean(stringValue) {
|
|||||||
case "yes":
|
case "yes":
|
||||||
case "on":
|
case "on":
|
||||||
case "1":
|
case "1":
|
||||||
return true;
|
return true
|
||||||
|
|
||||||
case "false":
|
case "false":
|
||||||
case "no":
|
case "no":
|
||||||
@ -28,45 +28,50 @@ function parseBoolean(stringValue) {
|
|||||||
case "none":
|
case "none":
|
||||||
case null:
|
case null:
|
||||||
case undefined:
|
case undefined:
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return Boolean(JSON.parse(stringValue));
|
return Boolean(JSON.parse(stringValue))
|
||||||
} catch {
|
} catch {
|
||||||
return Boolean(stringValue)
|
return Boolean(stringValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TASK_MAPPING = {
|
const TASK_MAPPING = {
|
||||||
prompt: { name: 'Prompt',
|
prompt: {
|
||||||
|
name: "Prompt",
|
||||||
setUI: (prompt) => {
|
setUI: (prompt) => {
|
||||||
promptField.value = prompt
|
promptField.value = prompt
|
||||||
},
|
},
|
||||||
readUI: () => promptField.value,
|
readUI: () => promptField.value,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
negative_prompt: { name: 'Negative Prompt',
|
negative_prompt: {
|
||||||
|
name: "Negative Prompt",
|
||||||
setUI: (negative_prompt) => {
|
setUI: (negative_prompt) => {
|
||||||
negativePromptField.value = negative_prompt
|
negativePromptField.value = negative_prompt
|
||||||
},
|
},
|
||||||
readUI: () => negativePromptField.value,
|
readUI: () => negativePromptField.value,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
active_tags: { name: "Image Modifiers",
|
active_tags: {
|
||||||
|
name: "Image Modifiers",
|
||||||
setUI: (active_tags) => {
|
setUI: (active_tags) => {
|
||||||
refreshModifiersState(active_tags)
|
refreshModifiersState(active_tags)
|
||||||
},
|
},
|
||||||
readUI: () => activeTags.map(x => x.name),
|
readUI: () => activeTags.map((x) => x.name),
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
inactive_tags: { name: "Inactive Image Modifiers",
|
inactive_tags: {
|
||||||
|
name: "Inactive Image Modifiers",
|
||||||
setUI: (inactive_tags) => {
|
setUI: (inactive_tags) => {
|
||||||
refreshInactiveTags(inactive_tags)
|
refreshInactiveTags(inactive_tags)
|
||||||
},
|
},
|
||||||
readUI: () => activeTags.filter(tag => tag.inactive === true).map(x => x.name),
|
readUI: () => activeTags.filter((tag) => tag.inactive === true).map((x) => x.name),
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
width: { name: 'Width',
|
width: {
|
||||||
|
name: "Width",
|
||||||
setUI: (width) => {
|
setUI: (width) => {
|
||||||
const oldVal = widthField.value
|
const oldVal = widthField.value
|
||||||
widthField.value = width
|
widthField.value = width
|
||||||
@ -77,7 +82,8 @@ const TASK_MAPPING = {
|
|||||||
readUI: () => parseInt(widthField.value),
|
readUI: () => parseInt(widthField.value),
|
||||||
parse: (val) => parseInt(val)
|
parse: (val) => parseInt(val)
|
||||||
},
|
},
|
||||||
height: { name: 'Height',
|
height: {
|
||||||
|
name: "Height",
|
||||||
setUI: (height) => {
|
setUI: (height) => {
|
||||||
const oldVal = heightField.value
|
const oldVal = heightField.value
|
||||||
heightField.value = height
|
heightField.value = height
|
||||||
@ -88,7 +94,8 @@ const TASK_MAPPING = {
|
|||||||
readUI: () => parseInt(heightField.value),
|
readUI: () => parseInt(heightField.value),
|
||||||
parse: (val) => parseInt(val)
|
parse: (val) => parseInt(val)
|
||||||
},
|
},
|
||||||
seed: { name: 'Seed',
|
seed: {
|
||||||
|
name: "Seed",
|
||||||
setUI: (seed) => {
|
setUI: (seed) => {
|
||||||
if (!seed) {
|
if (!seed) {
|
||||||
randomSeedField.checked = true
|
randomSeedField.checked = true
|
||||||
@ -97,21 +104,23 @@ const TASK_MAPPING = {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
randomSeedField.checked = false
|
randomSeedField.checked = false
|
||||||
randomSeedField.dispatchEvent(new Event('change')) // let plugins know that the state of the random seed toggle changed
|
randomSeedField.dispatchEvent(new Event("change")) // let plugins know that the state of the random seed toggle changed
|
||||||
seedField.disabled = false
|
seedField.disabled = false
|
||||||
seedField.value = seed
|
seedField.value = seed
|
||||||
},
|
},
|
||||||
readUI: () => parseInt(seedField.value), // just return the value the user is seeing in the UI
|
readUI: () => parseInt(seedField.value), // just return the value the user is seeing in the UI
|
||||||
parse: (val) => parseInt(val)
|
parse: (val) => parseInt(val)
|
||||||
},
|
},
|
||||||
num_inference_steps: { name: 'Steps',
|
num_inference_steps: {
|
||||||
|
name: "Steps",
|
||||||
setUI: (num_inference_steps) => {
|
setUI: (num_inference_steps) => {
|
||||||
numInferenceStepsField.value = num_inference_steps
|
numInferenceStepsField.value = num_inference_steps
|
||||||
},
|
},
|
||||||
readUI: () => parseInt(numInferenceStepsField.value),
|
readUI: () => parseInt(numInferenceStepsField.value),
|
||||||
parse: (val) => parseInt(val)
|
parse: (val) => parseInt(val)
|
||||||
},
|
},
|
||||||
guidance_scale: { name: 'Guidance Scale',
|
guidance_scale: {
|
||||||
|
name: "Guidance Scale",
|
||||||
setUI: (guidance_scale) => {
|
setUI: (guidance_scale) => {
|
||||||
guidanceScaleField.value = guidance_scale
|
guidanceScaleField.value = guidance_scale
|
||||||
updateGuidanceScaleSlider()
|
updateGuidanceScaleSlider()
|
||||||
@ -119,7 +128,8 @@ const TASK_MAPPING = {
|
|||||||
readUI: () => parseFloat(guidanceScaleField.value),
|
readUI: () => parseFloat(guidanceScaleField.value),
|
||||||
parse: (val) => parseFloat(val)
|
parse: (val) => parseFloat(val)
|
||||||
},
|
},
|
||||||
prompt_strength: { name: 'Prompt Strength',
|
prompt_strength: {
|
||||||
|
name: "Prompt Strength",
|
||||||
setUI: (prompt_strength) => {
|
setUI: (prompt_strength) => {
|
||||||
promptStrengthField.value = prompt_strength
|
promptStrengthField.value = prompt_strength
|
||||||
updatePromptStrengthSlider()
|
updatePromptStrengthSlider()
|
||||||
@ -128,16 +138,19 @@ const TASK_MAPPING = {
|
|||||||
parse: (val) => parseFloat(val)
|
parse: (val) => parseFloat(val)
|
||||||
},
|
},
|
||||||
|
|
||||||
init_image: { name: 'Initial Image',
|
init_image: {
|
||||||
|
name: "Initial Image",
|
||||||
setUI: (init_image) => {
|
setUI: (init_image) => {
|
||||||
initImagePreview.src = init_image
|
initImagePreview.src = init_image
|
||||||
},
|
},
|
||||||
readUI: () => initImagePreview.src,
|
readUI: () => initImagePreview.src,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
mask: { name: 'Mask',
|
mask: {
|
||||||
|
name: "Mask",
|
||||||
setUI: (mask) => {
|
setUI: (mask) => {
|
||||||
setTimeout(() => { // add a delay to insure this happens AFTER the main image loads (which reloads the inpainter)
|
setTimeout(() => {
|
||||||
|
// add a delay to insure this happens AFTER the main image loads (which reloads the inpainter)
|
||||||
imageInpainter.setImg(mask)
|
imageInpainter.setImg(mask)
|
||||||
}, 250)
|
}, 250)
|
||||||
maskSetting.checked = Boolean(mask)
|
maskSetting.checked = Boolean(mask)
|
||||||
@ -145,7 +158,8 @@ const TASK_MAPPING = {
|
|||||||
readUI: () => (maskSetting.checked ? imageInpainter.getImg() : undefined),
|
readUI: () => (maskSetting.checked ? imageInpainter.getImg() : undefined),
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
preserve_init_image_color_profile: { name: 'Preserve Color Profile',
|
preserve_init_image_color_profile: {
|
||||||
|
name: "Preserve Color Profile",
|
||||||
setUI: (preserve_init_image_color_profile) => {
|
setUI: (preserve_init_image_color_profile) => {
|
||||||
applyColorCorrectionField.checked = parseBoolean(preserve_init_image_color_profile)
|
applyColorCorrectionField.checked = parseBoolean(preserve_init_image_color_profile)
|
||||||
},
|
},
|
||||||
@ -153,14 +167,17 @@ const TASK_MAPPING = {
|
|||||||
parse: (val) => parseBoolean(val)
|
parse: (val) => parseBoolean(val)
|
||||||
},
|
},
|
||||||
|
|
||||||
use_face_correction: { name: 'Use Face Correction',
|
use_face_correction: {
|
||||||
|
name: "Use Face Correction",
|
||||||
setUI: (use_face_correction) => {
|
setUI: (use_face_correction) => {
|
||||||
const oldVal = gfpganModelField.value
|
const oldVal = gfpganModelField.value
|
||||||
gfpganModelField.value = getModelPath(use_face_correction, ['.pth'])
|
gfpganModelField.value = getModelPath(use_face_correction, [".pth"])
|
||||||
if (gfpganModelField.value) { // Is a valid value for the field.
|
if (gfpganModelField.value) {
|
||||||
|
// Is a valid value for the field.
|
||||||
useFaceCorrectionField.checked = true
|
useFaceCorrectionField.checked = true
|
||||||
gfpganModelField.disabled = false
|
gfpganModelField.disabled = false
|
||||||
} else { // Not a valid value, restore the old value and disable the filter.
|
} else {
|
||||||
|
// Not a valid value, restore the old value and disable the filter.
|
||||||
gfpganModelField.disabled = true
|
gfpganModelField.disabled = true
|
||||||
gfpganModelField.value = oldVal
|
gfpganModelField.value = oldVal
|
||||||
useFaceCorrectionField.checked = false
|
useFaceCorrectionField.checked = false
|
||||||
@ -171,15 +188,18 @@ const TASK_MAPPING = {
|
|||||||
readUI: () => (useFaceCorrectionField.checked ? gfpganModelField.value : undefined),
|
readUI: () => (useFaceCorrectionField.checked ? gfpganModelField.value : undefined),
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
use_upscale: { name: 'Use Upscaling',
|
use_upscale: {
|
||||||
|
name: "Use Upscaling",
|
||||||
setUI: (use_upscale) => {
|
setUI: (use_upscale) => {
|
||||||
const oldVal = upscaleModelField.value
|
const oldVal = upscaleModelField.value
|
||||||
upscaleModelField.value = getModelPath(use_upscale, ['.pth'])
|
upscaleModelField.value = getModelPath(use_upscale, [".pth"])
|
||||||
if (upscaleModelField.value) { // Is a valid value for the field.
|
if (upscaleModelField.value) {
|
||||||
|
// Is a valid value for the field.
|
||||||
useUpscalingField.checked = true
|
useUpscalingField.checked = true
|
||||||
upscaleModelField.disabled = false
|
upscaleModelField.disabled = false
|
||||||
upscaleAmountField.disabled = false
|
upscaleAmountField.disabled = false
|
||||||
} else { // Not a valid value, restore the old value and disable the filter.
|
} else {
|
||||||
|
// Not a valid value, restore the old value and disable the filter.
|
||||||
upscaleModelField.disabled = true
|
upscaleModelField.disabled = true
|
||||||
upscaleAmountField.disabled = true
|
upscaleAmountField.disabled = true
|
||||||
upscaleModelField.value = oldVal
|
upscaleModelField.value = oldVal
|
||||||
@ -189,25 +209,28 @@ const TASK_MAPPING = {
|
|||||||
readUI: () => (useUpscalingField.checked ? upscaleModelField.value : undefined),
|
readUI: () => (useUpscalingField.checked ? upscaleModelField.value : undefined),
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
upscale_amount: { name: 'Upscale By',
|
upscale_amount: {
|
||||||
|
name: "Upscale By",
|
||||||
setUI: (upscale_amount) => {
|
setUI: (upscale_amount) => {
|
||||||
upscaleAmountField.value = upscale_amount
|
upscaleAmountField.value = upscale_amount
|
||||||
},
|
},
|
||||||
readUI: () => upscaleAmountField.value,
|
readUI: () => upscaleAmountField.value,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
sampler_name: { name: 'Sampler',
|
sampler_name: {
|
||||||
|
name: "Sampler",
|
||||||
setUI: (sampler_name) => {
|
setUI: (sampler_name) => {
|
||||||
samplerField.value = sampler_name
|
samplerField.value = sampler_name
|
||||||
},
|
},
|
||||||
readUI: () => samplerField.value,
|
readUI: () => samplerField.value,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
use_stable_diffusion_model: { name: 'Stable Diffusion model',
|
use_stable_diffusion_model: {
|
||||||
|
name: "Stable Diffusion model",
|
||||||
setUI: (use_stable_diffusion_model) => {
|
setUI: (use_stable_diffusion_model) => {
|
||||||
const oldVal = stableDiffusionModelField.value
|
const oldVal = stableDiffusionModelField.value
|
||||||
|
|
||||||
use_stable_diffusion_model = getModelPath(use_stable_diffusion_model, ['.ckpt', '.safetensors'])
|
use_stable_diffusion_model = getModelPath(use_stable_diffusion_model, [".ckpt", ".safetensors"])
|
||||||
stableDiffusionModelField.value = use_stable_diffusion_model
|
stableDiffusionModelField.value = use_stable_diffusion_model
|
||||||
|
|
||||||
if (!stableDiffusionModelField.value) {
|
if (!stableDiffusionModelField.value) {
|
||||||
@ -217,35 +240,42 @@ const TASK_MAPPING = {
|
|||||||
readUI: () => stableDiffusionModelField.value,
|
readUI: () => stableDiffusionModelField.value,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
use_vae_model: { name: 'VAE model',
|
use_vae_model: {
|
||||||
|
name: "VAE model",
|
||||||
setUI: (use_vae_model) => {
|
setUI: (use_vae_model) => {
|
||||||
const oldVal = vaeModelField.value
|
const oldVal = vaeModelField.value
|
||||||
use_vae_model = (use_vae_model === undefined || use_vae_model === null || use_vae_model === 'None' ? '' : use_vae_model)
|
use_vae_model =
|
||||||
|
use_vae_model === undefined || use_vae_model === null || use_vae_model === "None" ? "" : use_vae_model
|
||||||
|
|
||||||
if (use_vae_model !== '') {
|
if (use_vae_model !== "") {
|
||||||
use_vae_model = getModelPath(use_vae_model, ['.vae.pt', '.ckpt'])
|
use_vae_model = getModelPath(use_vae_model, [".vae.pt", ".ckpt"])
|
||||||
use_vae_model = use_vae_model !== '' ? use_vae_model : oldVal
|
use_vae_model = use_vae_model !== "" ? use_vae_model : oldVal
|
||||||
}
|
}
|
||||||
vaeModelField.value = use_vae_model
|
vaeModelField.value = use_vae_model
|
||||||
},
|
},
|
||||||
readUI: () => vaeModelField.value,
|
readUI: () => vaeModelField.value,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
use_lora_model: { name: 'LoRA model',
|
use_lora_model: {
|
||||||
|
name: "LoRA model",
|
||||||
setUI: (use_lora_model) => {
|
setUI: (use_lora_model) => {
|
||||||
const oldVal = loraModelField.value
|
const oldVal = loraModelField.value
|
||||||
use_lora_model = (use_lora_model === undefined || use_lora_model === null || use_lora_model === 'None' ? '' : use_lora_model)
|
use_lora_model =
|
||||||
|
use_lora_model === undefined || use_lora_model === null || use_lora_model === "None"
|
||||||
|
? ""
|
||||||
|
: use_lora_model
|
||||||
|
|
||||||
if (use_lora_model !== '') {
|
if (use_lora_model !== "") {
|
||||||
use_lora_model = getModelPath(use_lora_model, ['.ckpt', '.safetensors'])
|
use_lora_model = getModelPath(use_lora_model, [".ckpt", ".safetensors"])
|
||||||
use_lora_model = use_lora_model !== '' ? use_lora_model : oldVal
|
use_lora_model = use_lora_model !== "" ? use_lora_model : oldVal
|
||||||
}
|
}
|
||||||
loraModelField.value = use_lora_model
|
loraModelField.value = use_lora_model
|
||||||
},
|
},
|
||||||
readUI: () => loraModelField.value,
|
readUI: () => loraModelField.value,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
lora_alpha: { name: 'LoRA Strength',
|
lora_alpha: {
|
||||||
|
name: "LoRA Strength",
|
||||||
setUI: (lora_alpha) => {
|
setUI: (lora_alpha) => {
|
||||||
loraAlphaField.value = lora_alpha
|
loraAlphaField.value = lora_alpha
|
||||||
updateLoraAlphaSlider()
|
updateLoraAlphaSlider()
|
||||||
@ -253,22 +283,29 @@ const TASK_MAPPING = {
|
|||||||
readUI: () => parseFloat(loraAlphaField.value),
|
readUI: () => parseFloat(loraAlphaField.value),
|
||||||
parse: (val) => parseFloat(val)
|
parse: (val) => parseFloat(val)
|
||||||
},
|
},
|
||||||
use_hypernetwork_model: { name: 'Hypernetwork model',
|
use_hypernetwork_model: {
|
||||||
|
name: "Hypernetwork model",
|
||||||
setUI: (use_hypernetwork_model) => {
|
setUI: (use_hypernetwork_model) => {
|
||||||
const oldVal = hypernetworkModelField.value
|
const oldVal = hypernetworkModelField.value
|
||||||
use_hypernetwork_model = (use_hypernetwork_model === undefined || use_hypernetwork_model === null || use_hypernetwork_model === 'None' ? '' : use_hypernetwork_model)
|
use_hypernetwork_model =
|
||||||
|
use_hypernetwork_model === undefined ||
|
||||||
|
use_hypernetwork_model === null ||
|
||||||
|
use_hypernetwork_model === "None"
|
||||||
|
? ""
|
||||||
|
: use_hypernetwork_model
|
||||||
|
|
||||||
if (use_hypernetwork_model !== '') {
|
if (use_hypernetwork_model !== "") {
|
||||||
use_hypernetwork_model = getModelPath(use_hypernetwork_model, ['.pt'])
|
use_hypernetwork_model = getModelPath(use_hypernetwork_model, [".pt"])
|
||||||
use_hypernetwork_model = use_hypernetwork_model !== '' ? use_hypernetwork_model : oldVal
|
use_hypernetwork_model = use_hypernetwork_model !== "" ? use_hypernetwork_model : oldVal
|
||||||
}
|
}
|
||||||
hypernetworkModelField.value = use_hypernetwork_model
|
hypernetworkModelField.value = use_hypernetwork_model
|
||||||
hypernetworkModelField.dispatchEvent(new Event('change'))
|
hypernetworkModelField.dispatchEvent(new Event("change"))
|
||||||
},
|
},
|
||||||
readUI: () => hypernetworkModelField.value,
|
readUI: () => hypernetworkModelField.value,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
hypernetwork_strength: { name: 'Hypernetwork Strength',
|
hypernetwork_strength: {
|
||||||
|
name: "Hypernetwork Strength",
|
||||||
setUI: (hypernetwork_strength) => {
|
setUI: (hypernetwork_strength) => {
|
||||||
hypernetworkStrengthField.value = hypernetwork_strength
|
hypernetworkStrengthField.value = hypernetwork_strength
|
||||||
updateHypernetworkStrengthSlider()
|
updateHypernetworkStrengthSlider()
|
||||||
@ -277,7 +314,8 @@ const TASK_MAPPING = {
|
|||||||
parse: (val) => parseFloat(val)
|
parse: (val) => parseFloat(val)
|
||||||
},
|
},
|
||||||
|
|
||||||
num_outputs: { name: 'Parallel Images',
|
num_outputs: {
|
||||||
|
name: "Parallel Images",
|
||||||
setUI: (num_outputs) => {
|
setUI: (num_outputs) => {
|
||||||
numOutputsParallelField.value = num_outputs
|
numOutputsParallelField.value = num_outputs
|
||||||
},
|
},
|
||||||
@ -285,7 +323,8 @@ const TASK_MAPPING = {
|
|||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
|
|
||||||
use_cpu: { name: 'Use CPU',
|
use_cpu: {
|
||||||
|
name: "Use CPU",
|
||||||
setUI: (use_cpu) => {
|
setUI: (use_cpu) => {
|
||||||
useCPUField.checked = use_cpu
|
useCPUField.checked = use_cpu
|
||||||
},
|
},
|
||||||
@ -293,28 +332,32 @@ const TASK_MAPPING = {
|
|||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
|
|
||||||
stream_image_progress: { name: 'Stream Image Progress',
|
stream_image_progress: {
|
||||||
|
name: "Stream Image Progress",
|
||||||
setUI: (stream_image_progress) => {
|
setUI: (stream_image_progress) => {
|
||||||
streamImageProgressField.checked = (parseInt(numOutputsTotalField.value) > 50 ? false : stream_image_progress)
|
streamImageProgressField.checked = parseInt(numOutputsTotalField.value) > 50 ? false : stream_image_progress
|
||||||
},
|
},
|
||||||
readUI: () => streamImageProgressField.checked,
|
readUI: () => streamImageProgressField.checked,
|
||||||
parse: (val) => Boolean(val)
|
parse: (val) => Boolean(val)
|
||||||
},
|
},
|
||||||
show_only_filtered_image: { name: 'Show only the corrected/upscaled image',
|
show_only_filtered_image: {
|
||||||
|
name: "Show only the corrected/upscaled image",
|
||||||
setUI: (show_only_filtered_image) => {
|
setUI: (show_only_filtered_image) => {
|
||||||
showOnlyFilteredImageField.checked = show_only_filtered_image
|
showOnlyFilteredImageField.checked = show_only_filtered_image
|
||||||
},
|
},
|
||||||
readUI: () => showOnlyFilteredImageField.checked,
|
readUI: () => showOnlyFilteredImageField.checked,
|
||||||
parse: (val) => Boolean(val)
|
parse: (val) => Boolean(val)
|
||||||
},
|
},
|
||||||
output_format: { name: 'Output Format',
|
output_format: {
|
||||||
|
name: "Output Format",
|
||||||
setUI: (output_format) => {
|
setUI: (output_format) => {
|
||||||
outputFormatField.value = output_format
|
outputFormatField.value = output_format
|
||||||
},
|
},
|
||||||
readUI: () => outputFormatField.value,
|
readUI: () => outputFormatField.value,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
save_to_disk_path: { name: 'Save to disk path',
|
save_to_disk_path: {
|
||||||
|
name: "Save to disk path",
|
||||||
setUI: (save_to_disk_path) => {
|
setUI: (save_to_disk_path) => {
|
||||||
saveToDiskField.checked = Boolean(save_to_disk_path)
|
saveToDiskField.checked = Boolean(save_to_disk_path)
|
||||||
diskPathField.value = save_to_disk_path
|
diskPathField.value = save_to_disk_path
|
||||||
@ -327,14 +370,14 @@ const TASK_MAPPING = {
|
|||||||
function restoreTaskToUI(task, fieldsToSkip) {
|
function restoreTaskToUI(task, fieldsToSkip) {
|
||||||
fieldsToSkip = fieldsToSkip || []
|
fieldsToSkip = fieldsToSkip || []
|
||||||
|
|
||||||
if ('numOutputsTotal' in task) {
|
if ("numOutputsTotal" in task) {
|
||||||
numOutputsTotalField.value = task.numOutputsTotal
|
numOutputsTotalField.value = task.numOutputsTotal
|
||||||
}
|
}
|
||||||
if ('seed' in task) {
|
if ("seed" in task) {
|
||||||
randomSeedField.checked = false
|
randomSeedField.checked = false
|
||||||
seedField.value = task.seed
|
seedField.value = task.seed
|
||||||
}
|
}
|
||||||
if (!('reqBody' in task)) {
|
if (!("reqBody" in task)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for (const key in TASK_MAPPING) {
|
for (const key in TASK_MAPPING) {
|
||||||
@ -344,31 +387,31 @@ function restoreTaskToUI(task, fieldsToSkip) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// properly reset fields not present in the task
|
// properly reset fields not present in the task
|
||||||
if (!('use_hypernetwork_model' in task.reqBody)) {
|
if (!("use_hypernetwork_model" in task.reqBody)) {
|
||||||
hypernetworkModelField.value = ""
|
hypernetworkModelField.value = ""
|
||||||
hypernetworkModelField.dispatchEvent(new Event("change"))
|
hypernetworkModelField.dispatchEvent(new Event("change"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!('use_lora_model' in task.reqBody)) {
|
if (!("use_lora_model" in task.reqBody)) {
|
||||||
loraModelField.value = ""
|
loraModelField.value = ""
|
||||||
loraModelField.dispatchEvent(new Event("change"))
|
loraModelField.dispatchEvent(new Event("change"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore the original prompt if provided (e.g. use settings), fallback to prompt as needed (e.g. copy/paste or d&d)
|
// 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
|
promptField.value = task.reqBody.original_prompt
|
||||||
if (!('original_prompt' in task.reqBody)) {
|
if (!("original_prompt" in task.reqBody)) {
|
||||||
promptField.value = task.reqBody.prompt
|
promptField.value = task.reqBody.prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
// properly reset checkboxes
|
// properly reset checkboxes
|
||||||
if (!('use_face_correction' in task.reqBody)) {
|
if (!("use_face_correction" in task.reqBody)) {
|
||||||
useFaceCorrectionField.checked = false
|
useFaceCorrectionField.checked = false
|
||||||
gfpganModelField.disabled = true
|
gfpganModelField.disabled = true
|
||||||
}
|
}
|
||||||
if (!('use_upscale' in task.reqBody)) {
|
if (!("use_upscale" in task.reqBody)) {
|
||||||
useUpscalingField.checked = false
|
useUpscalingField.checked = false
|
||||||
}
|
}
|
||||||
if (!('mask' in task.reqBody) && maskSetting.checked) {
|
if (!("mask" in task.reqBody) && maskSetting.checked) {
|
||||||
maskSetting.checked = false
|
maskSetting.checked = false
|
||||||
maskSetting.dispatchEvent(new Event("click"))
|
maskSetting.dispatchEvent(new Event("click"))
|
||||||
}
|
}
|
||||||
@ -379,15 +422,18 @@ function restoreTaskToUI(task, fieldsToSkip) {
|
|||||||
if (IMAGE_REGEX.test(initImagePreview.src) && task.reqBody.init_image == undefined) {
|
if (IMAGE_REGEX.test(initImagePreview.src) && task.reqBody.init_image == undefined) {
|
||||||
// hide source image
|
// hide source image
|
||||||
initImageClearBtn.dispatchEvent(new Event("click"))
|
initImageClearBtn.dispatchEvent(new Event("click"))
|
||||||
}
|
} else if (task.reqBody.init_image !== undefined) {
|
||||||
else if (task.reqBody.init_image !== undefined) {
|
|
||||||
// listen for inpainter loading event, which happens AFTER the main image loads (which reloads the inpainter)
|
// listen for inpainter loading event, which happens AFTER the main image loads (which reloads the inpainter)
|
||||||
initImagePreview.addEventListener('load', function() {
|
initImagePreview.addEventListener(
|
||||||
|
"load",
|
||||||
|
function() {
|
||||||
if (Boolean(task.reqBody.mask)) {
|
if (Boolean(task.reqBody.mask)) {
|
||||||
imageInpainter.setImg(task.reqBody.mask)
|
imageInpainter.setImg(task.reqBody.mask)
|
||||||
maskSetting.checked = true
|
maskSetting.checked = true
|
||||||
}
|
}
|
||||||
}, { once: true })
|
},
|
||||||
|
{ once: true }
|
||||||
|
)
|
||||||
initImagePreview.src = task.reqBody.init_image
|
initImagePreview.src = task.reqBody.init_image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -397,28 +443,26 @@ function readUI() {
|
|||||||
reqBody[key] = TASK_MAPPING[key].readUI()
|
reqBody[key] = TASK_MAPPING[key].readUI()
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
'numOutputsTotal': parseInt(numOutputsTotalField.value),
|
numOutputsTotal: parseInt(numOutputsTotalField.value),
|
||||||
'seed': TASK_MAPPING['seed'].readUI(),
|
seed: TASK_MAPPING["seed"].readUI(),
|
||||||
'reqBody': reqBody
|
reqBody: reqBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getModelPath(filename, extensions)
|
function getModelPath(filename, extensions) {
|
||||||
{
|
|
||||||
if (typeof filename !== "string") {
|
if (typeof filename !== "string") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let pathIdx
|
let pathIdx
|
||||||
if (filename.includes('/models/stable-diffusion/')) {
|
if (filename.includes("/models/stable-diffusion/")) {
|
||||||
pathIdx = filename.indexOf('/models/stable-diffusion/') + 25 // Linux, Mac paths
|
pathIdx = filename.indexOf("/models/stable-diffusion/") + 25 // Linux, Mac paths
|
||||||
}
|
} else if (filename.includes("\\models\\stable-diffusion\\")) {
|
||||||
else if (filename.includes('\\models\\stable-diffusion\\')) {
|
pathIdx = filename.indexOf("\\models\\stable-diffusion\\") + 25 // Linux, Mac paths
|
||||||
pathIdx = filename.indexOf('\\models\\stable-diffusion\\') + 25 // Linux, Mac paths
|
|
||||||
}
|
}
|
||||||
if (pathIdx >= 0) {
|
if (pathIdx >= 0) {
|
||||||
filename = filename.slice(pathIdx)
|
filename = filename.slice(pathIdx)
|
||||||
}
|
}
|
||||||
extensions.forEach(ext => {
|
extensions.forEach((ext) => {
|
||||||
if (filename.endsWith(ext)) {
|
if (filename.endsWith(ext)) {
|
||||||
filename = filename.slice(0, filename.length - ext.length)
|
filename = filename.slice(0, filename.length - ext.length)
|
||||||
}
|
}
|
||||||
@ -427,26 +471,26 @@ function getModelPath(filename, extensions)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TASK_TEXT_MAPPING = {
|
const TASK_TEXT_MAPPING = {
|
||||||
prompt: 'Prompt',
|
prompt: "Prompt",
|
||||||
width: 'Width',
|
width: "Width",
|
||||||
height: 'Height',
|
height: "Height",
|
||||||
seed: 'Seed',
|
seed: "Seed",
|
||||||
num_inference_steps: 'Steps',
|
num_inference_steps: "Steps",
|
||||||
guidance_scale: 'Guidance Scale',
|
guidance_scale: "Guidance Scale",
|
||||||
prompt_strength: 'Prompt Strength',
|
prompt_strength: "Prompt Strength",
|
||||||
use_face_correction: 'Use Face Correction',
|
use_face_correction: "Use Face Correction",
|
||||||
use_upscale: 'Use Upscaling',
|
use_upscale: "Use Upscaling",
|
||||||
upscale_amount: 'Upscale By',
|
upscale_amount: "Upscale By",
|
||||||
sampler_name: 'Sampler',
|
sampler_name: "Sampler",
|
||||||
negative_prompt: 'Negative Prompt',
|
negative_prompt: "Negative Prompt",
|
||||||
use_stable_diffusion_model: 'Stable Diffusion model',
|
use_stable_diffusion_model: "Stable Diffusion model",
|
||||||
use_hypernetwork_model: 'Hypernetwork model',
|
use_hypernetwork_model: "Hypernetwork model",
|
||||||
hypernetwork_strength: 'Hypernetwork Strength'
|
hypernetwork_strength: "Hypernetwork Strength"
|
||||||
}
|
}
|
||||||
function parseTaskFromText(str) {
|
function parseTaskFromText(str) {
|
||||||
const taskReqBody = {}
|
const taskReqBody = {}
|
||||||
|
|
||||||
const lines = str.split('\n')
|
const lines = str.split("\n")
|
||||||
if (lines.length === 0) {
|
if (lines.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -454,14 +498,14 @@ function parseTaskFromText(str) {
|
|||||||
// Prompt
|
// Prompt
|
||||||
let knownKeyOnFirstLine = false
|
let knownKeyOnFirstLine = false
|
||||||
for (let key in TASK_TEXT_MAPPING) {
|
for (let key in TASK_TEXT_MAPPING) {
|
||||||
if (lines[0].startsWith(TASK_TEXT_MAPPING[key] + ':')) {
|
if (lines[0].startsWith(TASK_TEXT_MAPPING[key] + ":")) {
|
||||||
knownKeyOnFirstLine = true
|
knownKeyOnFirstLine = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!knownKeyOnFirstLine) {
|
if (!knownKeyOnFirstLine) {
|
||||||
taskReqBody.prompt = lines[0]
|
taskReqBody.prompt = lines[0]
|
||||||
console.log('Prompt:', taskReqBody.prompt)
|
console.log("Prompt:", taskReqBody.prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key in TASK_TEXT_MAPPING) {
|
for (const key in TASK_TEXT_MAPPING) {
|
||||||
@ -469,18 +513,18 @@ function parseTaskFromText(str) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = TASK_TEXT_MAPPING[key];
|
const name = TASK_TEXT_MAPPING[key]
|
||||||
let val = undefined
|
let val = undefined
|
||||||
|
|
||||||
const reName = new RegExp(`${name}\\ *:\\ *(.*)(?:\\r\\n|\\r|\\n)*`, 'igm')
|
const reName = new RegExp(`${name}\\ *:\\ *(.*)(?:\\r\\n|\\r|\\n)*`, "igm")
|
||||||
const match = reName.exec(str);
|
const match = reName.exec(str)
|
||||||
if (match) {
|
if (match) {
|
||||||
str = str.slice(0, match.index) + str.slice(match.index + match[0].length)
|
str = str.slice(0, match.index) + str.slice(match.index + match[0].length)
|
||||||
val = match[1]
|
val = match[1]
|
||||||
}
|
}
|
||||||
if (val !== undefined) {
|
if (val !== undefined) {
|
||||||
taskReqBody[key] = TASK_MAPPING[key].parse(val.trim())
|
taskReqBody[key] = TASK_MAPPING[key].parse(val.trim())
|
||||||
console.log(TASK_MAPPING[key].name + ':', taskReqBody[key])
|
console.log(TASK_MAPPING[key].name + ":", taskReqBody[key])
|
||||||
if (!str) {
|
if (!str) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -490,18 +534,19 @@ function parseTaskFromText(str) {
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
const task = { reqBody: taskReqBody }
|
const task = { reqBody: taskReqBody }
|
||||||
if ('seed' in taskReqBody) {
|
if ("seed" in taskReqBody) {
|
||||||
task.seed = taskReqBody.seed
|
task.seed = taskReqBody.seed
|
||||||
}
|
}
|
||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parseContent(text) {
|
async function parseContent(text) {
|
||||||
text = text.trim();
|
text = text.trim()
|
||||||
if (text.startsWith('{') && text.endsWith('}')) {
|
if (text.startsWith("{") && text.endsWith("}")) {
|
||||||
try {
|
try {
|
||||||
const task = JSON.parse(text)
|
const task = JSON.parse(text)
|
||||||
if (!('reqBody' in task)) { // support the format saved to the disk, by the UI
|
if (!("reqBody" in task)) {
|
||||||
|
// support the format saved to the disk, by the UI
|
||||||
task.reqBody = Object.assign({}, task)
|
task.reqBody = Object.assign({}, task)
|
||||||
}
|
}
|
||||||
restoreTaskToUI(task)
|
restoreTaskToUI(task)
|
||||||
@ -513,7 +558,8 @@ async function parseContent(text) {
|
|||||||
}
|
}
|
||||||
// Normal txt file.
|
// Normal txt file.
|
||||||
const task = parseTaskFromText(text)
|
const task = parseTaskFromText(text)
|
||||||
if (text.toLowerCase().includes('seed:') && task) { // only parse valid task content
|
if (text.toLowerCase().includes("seed:") && task) {
|
||||||
|
// only parse valid task content
|
||||||
restoreTaskToUI(task)
|
restoreTaskToUI(task)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
@ -530,21 +576,25 @@ async function readFile(file, i) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function dropHandler(ev) {
|
function dropHandler(ev) {
|
||||||
console.log('Content dropped...')
|
console.log("Content dropped...")
|
||||||
let items = []
|
let items = []
|
||||||
|
|
||||||
if (ev?.dataTransfer?.items) { // Use DataTransferItemList interface
|
if (ev?.dataTransfer?.items) {
|
||||||
|
// Use DataTransferItemList interface
|
||||||
items = Array.from(ev.dataTransfer.items)
|
items = Array.from(ev.dataTransfer.items)
|
||||||
items = items.filter(item => item.kind === 'file')
|
items = items.filter((item) => item.kind === "file")
|
||||||
items = items.map(item => item.getAsFile())
|
items = items.map((item) => item.getAsFile())
|
||||||
} else if (ev?.dataTransfer?.files) { // Use DataTransfer interface
|
} else if (ev?.dataTransfer?.files) {
|
||||||
|
// Use DataTransfer interface
|
||||||
items = Array.from(ev.dataTransfer.files)
|
items = Array.from(ev.dataTransfer.files)
|
||||||
}
|
}
|
||||||
|
|
||||||
items.forEach(item => {item.file_ext = EXT_REGEX.exec(item.name.toLowerCase())[1]})
|
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 text_items = items.filter((item) => TEXT_EXTENSIONS.includes(item.file_ext))
|
||||||
let image_items = items.filter(item => IMAGE_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) {
|
if (image_items.length > 0 && ev.target == initImageSelector) {
|
||||||
return // let the event bubble up, so that the Init Image filepicker can receive this
|
return // let the event bubble up, so that the Init Image filepicker can receive this
|
||||||
@ -554,7 +604,7 @@ function dropHandler(ev) {
|
|||||||
text_items.forEach(readFile)
|
text_items.forEach(readFile)
|
||||||
}
|
}
|
||||||
function dragOverHandler(ev) {
|
function dragOverHandler(ev) {
|
||||||
console.log('Content in drop zone')
|
console.log("Content in drop zone")
|
||||||
|
|
||||||
// Prevent default behavior (Prevent file/content from being opened)
|
// Prevent default behavior (Prevent file/content from being opened)
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
@ -562,53 +612,52 @@ function dragOverHandler(ev) {
|
|||||||
ev.dataTransfer.dropEffect = "copy"
|
ev.dataTransfer.dropEffect = "copy"
|
||||||
|
|
||||||
let img = new Image()
|
let img = new Image()
|
||||||
img.src = '//' + location.host + '/media/images/favicon-32x32.png'
|
img.src = "//" + location.host + "/media/images/favicon-32x32.png"
|
||||||
ev.dataTransfer.setDragImage(img, 16, 16)
|
ev.dataTransfer.setDragImage(img, 16, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("drop", dropHandler)
|
document.addEventListener("drop", dropHandler)
|
||||||
document.addEventListener("dragover", dragOverHandler)
|
document.addEventListener("dragover", dragOverHandler)
|
||||||
|
|
||||||
const TASK_REQ_NO_EXPORT = [
|
const TASK_REQ_NO_EXPORT = ["use_cpu", "save_to_disk_path"]
|
||||||
"use_cpu",
|
const resetSettings = document.getElementById("reset-image-settings")
|
||||||
"save_to_disk_path"
|
|
||||||
]
|
|
||||||
const resetSettings = document.getElementById('reset-image-settings')
|
|
||||||
|
|
||||||
function checkReadTextClipboardPermission(result) {
|
function checkReadTextClipboardPermission(result) {
|
||||||
if (result.state != "granted" && result.state != "prompt") {
|
if (result.state != "granted" && result.state != "prompt") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// PASTE ICON
|
// PASTE ICON
|
||||||
const pasteIcon = document.createElement('i')
|
const pasteIcon = document.createElement("i")
|
||||||
pasteIcon.className = 'fa-solid fa-paste section-button'
|
pasteIcon.className = "fa-solid fa-paste section-button"
|
||||||
pasteIcon.innerHTML = `<span class="simple-tooltip top-left">Paste Image Settings</span>`
|
pasteIcon.innerHTML = `<span class="simple-tooltip top-left">Paste Image Settings</span>`
|
||||||
pasteIcon.addEventListener('click', async (event) => {
|
pasteIcon.addEventListener("click", async (event) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
// Add css class 'active'
|
// Add css class 'active'
|
||||||
pasteIcon.classList.add('active')
|
pasteIcon.classList.add("active")
|
||||||
// In 350 ms remove the 'active' class
|
// In 350 ms remove the 'active' class
|
||||||
asyncDelay(350).then(() => pasteIcon.classList.remove('active'))
|
asyncDelay(350).then(() => pasteIcon.classList.remove("active"))
|
||||||
|
|
||||||
// Retrieve clipboard content and try to parse it
|
// Retrieve clipboard content and try to parse it
|
||||||
const text = await navigator.clipboard.readText();
|
const text = await navigator.clipboard.readText()
|
||||||
await parseContent(text)
|
await parseContent(text)
|
||||||
})
|
})
|
||||||
resetSettings.parentNode.insertBefore(pasteIcon, resetSettings)
|
resetSettings.parentNode.insertBefore(pasteIcon, resetSettings)
|
||||||
}
|
}
|
||||||
navigator.permissions.query({ name: "clipboard-read" }).then(checkReadTextClipboardPermission, (reason) => console.log('clipboard-read is not available. %o', reason))
|
navigator.permissions
|
||||||
|
.query({ name: "clipboard-read" })
|
||||||
|
.then(checkReadTextClipboardPermission, (reason) => console.log("clipboard-read is not available. %o", reason))
|
||||||
|
|
||||||
document.addEventListener('paste', async (event) => {
|
document.addEventListener("paste", async (event) => {
|
||||||
if (event.target) {
|
if (event.target) {
|
||||||
const targetTag = event.target.tagName.toLowerCase()
|
const targetTag = event.target.tagName.toLowerCase()
|
||||||
// Disable when targeting input elements.
|
// Disable when targeting input elements.
|
||||||
if (targetTag === 'input' || targetTag === 'textarea') {
|
if (targetTag === "input" || targetTag === "textarea") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const paste = (event.clipboardData || window.clipboardData).getData('text')
|
const paste = (event.clipboardData || window.clipboardData).getData("text")
|
||||||
const selection = window.getSelection()
|
const selection = window.getSelection()
|
||||||
if (paste != "" && selection.toString().trim().length <= 0 && await parseContent(paste)) {
|
if (paste != "" && selection.toString().trim().length <= 0 && (await parseContent(paste))) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -620,15 +669,15 @@ function checkWriteToClipboardPermission (result) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// COPY ICON
|
// COPY ICON
|
||||||
const copyIcon = document.createElement('i')
|
const copyIcon = document.createElement("i")
|
||||||
copyIcon.className = 'fa-solid fa-clipboard section-button'
|
copyIcon.className = "fa-solid fa-clipboard section-button"
|
||||||
copyIcon.innerHTML = `<span class="simple-tooltip top-left">Copy Image Settings</span>`
|
copyIcon.innerHTML = `<span class="simple-tooltip top-left">Copy Image Settings</span>`
|
||||||
copyIcon.addEventListener('click', (event) => {
|
copyIcon.addEventListener("click", (event) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
// Add css class 'active'
|
// Add css class 'active'
|
||||||
copyIcon.classList.add('active')
|
copyIcon.classList.add("active")
|
||||||
// In 350 ms remove the 'active' class
|
// In 350 ms remove the 'active' class
|
||||||
asyncDelay(350).then(() => copyIcon.classList.remove('active'))
|
asyncDelay(350).then(() => copyIcon.classList.remove("active"))
|
||||||
const uiState = readUI()
|
const uiState = readUI()
|
||||||
TASK_REQ_NO_EXPORT.forEach((key) => delete uiState.reqBody[key])
|
TASK_REQ_NO_EXPORT.forEach((key) => delete uiState.reqBody[key])
|
||||||
if (uiState.reqBody.init_image && !IMAGE_REGEX.test(uiState.reqBody.init_image)) {
|
if (uiState.reqBody.init_image && !IMAGE_REGEX.test(uiState.reqBody.init_image)) {
|
||||||
@ -641,7 +690,7 @@ function checkWriteToClipboardPermission (result) {
|
|||||||
}
|
}
|
||||||
// Determine which access we have to the clipboard. Clipboard access is only available on localhost or via TLS.
|
// 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) => {
|
navigator.permissions.query({ name: "clipboard-write" }).then(checkWriteToClipboardPermission, (e) => {
|
||||||
if (e instanceof TypeError && typeof navigator?.clipboard?.writeText === 'function') {
|
if (e instanceof TypeError && typeof navigator?.clipboard?.writeText === "function") {
|
||||||
// Fix for firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1560373
|
// Fix for firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1560373
|
||||||
checkWriteToClipboardPermission({ state: "granted" })
|
checkWriteToClipboardPermission({ state: "granted" })
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -6,14 +6,14 @@ const IMAGE_EDITOR_BUTTONS = [
|
|||||||
{
|
{
|
||||||
name: "Cancel",
|
name: "Cancel",
|
||||||
icon: "fa-regular fa-circle-xmark",
|
icon: "fa-regular fa-circle-xmark",
|
||||||
handler: editor => {
|
handler: (editor) => {
|
||||||
editor.hide()
|
editor.hide()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Save",
|
name: "Save",
|
||||||
icon: "fa-solid fa-floppy-disk",
|
icon: "fa-solid fa-floppy-disk",
|
||||||
handler: editor => {
|
handler: (editor) => {
|
||||||
editor.saveImage()
|
editor.saveImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,9 +104,9 @@ const IMAGE_EDITOR_TOOLS = [
|
|||||||
var drawn_rgb = editor.ctx_current.getImageData(x, y, 1, 1).data
|
var drawn_rgb = editor.ctx_current.getImageData(x, y, 1, 1).data
|
||||||
var drawn_opacity = drawn_rgb[3] / 255
|
var drawn_opacity = drawn_rgb[3] / 255
|
||||||
editor.custom_color_input.value = rgbToHex({
|
editor.custom_color_input.value = rgbToHex({
|
||||||
r: (drawn_rgb[0] * drawn_opacity) + (img_rgb[0] * (1 - drawn_opacity)),
|
r: drawn_rgb[0] * drawn_opacity + img_rgb[0] * (1 - drawn_opacity),
|
||||||
g: (drawn_rgb[1] * drawn_opacity) + (img_rgb[1] * (1 - drawn_opacity)),
|
g: drawn_rgb[1] * drawn_opacity + img_rgb[1] * (1 - drawn_opacity),
|
||||||
b: (drawn_rgb[2] * drawn_opacity) + (img_rgb[2] * (1 - drawn_opacity)),
|
b: drawn_rgb[2] * drawn_opacity + img_rgb[2] * (1 - drawn_opacity)
|
||||||
})
|
})
|
||||||
editor.custom_color_input.dispatchEvent(new Event("change"))
|
editor.custom_color_input.dispatchEvent(new Event("change"))
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ const IMAGE_EDITOR_ACTIONS = [
|
|||||||
className: "load_mask",
|
className: "load_mask",
|
||||||
icon: "fa-regular fa-folder-open",
|
icon: "fa-regular fa-folder-open",
|
||||||
handler: (editor) => {
|
handler: (editor) => {
|
||||||
let el = document.createElement('input')
|
let el = document.createElement("input")
|
||||||
el.setAttribute("type", "file")
|
el.setAttribute("type", "file")
|
||||||
el.addEventListener("change", function() {
|
el.addEventListener("change", function() {
|
||||||
if (this.files.length === 0) {
|
if (this.files.length === 0) {
|
||||||
@ -133,7 +133,7 @@ const IMAGE_EDITOR_ACTIONS = [
|
|||||||
let reader = new FileReader()
|
let reader = new FileReader()
|
||||||
let file = this.files[0]
|
let file = this.files[0]
|
||||||
|
|
||||||
reader.addEventListener('load', function(event) {
|
reader.addEventListener("load", function(event) {
|
||||||
let maskData = reader.result
|
let maskData = reader.result
|
||||||
|
|
||||||
editor.layers.drawing.ctx.clearRect(0, 0, editor.width, editor.height)
|
editor.layers.drawing.ctx.clearRect(0, 0, editor.width, editor.height)
|
||||||
@ -200,13 +200,13 @@ var IMAGE_EDITOR_SECTIONS = [
|
|||||||
name: "tool",
|
name: "tool",
|
||||||
title: "Tool",
|
title: "Tool",
|
||||||
default: "draw",
|
default: "draw",
|
||||||
options: Array.from(IMAGE_EDITOR_TOOLS.map(t => t.id)),
|
options: Array.from(IMAGE_EDITOR_TOOLS.map((t) => t.id)),
|
||||||
initElement: (element, option) => {
|
initElement: (element, option) => {
|
||||||
var tool_info = IMAGE_EDITOR_TOOLS.find(t => t.id == option)
|
var tool_info = IMAGE_EDITOR_TOOLS.find((t) => t.id == option)
|
||||||
element.className = "image-editor-button button"
|
element.className = "image-editor-button button"
|
||||||
var sub_element = document.createElement("div")
|
var sub_element = document.createElement("div")
|
||||||
var icon = document.createElement("i")
|
var icon = document.createElement("i")
|
||||||
tool_info.icon.split(" ").forEach(c => icon.classList.add(c))
|
tool_info.icon.split(" ").forEach((c) => icon.classList.add(c))
|
||||||
sub_element.appendChild(icon)
|
sub_element.appendChild(icon)
|
||||||
sub_element.append(tool_info.name)
|
sub_element.append(tool_info.name)
|
||||||
element.appendChild(sub_element)
|
element.appendChild(sub_element)
|
||||||
@ -218,14 +218,46 @@ var IMAGE_EDITOR_SECTIONS = [
|
|||||||
default: "#f1c232",
|
default: "#f1c232",
|
||||||
options: [
|
options: [
|
||||||
"custom",
|
"custom",
|
||||||
"#ea9999", "#e06666", "#cc0000", "#990000", "#660000",
|
"#ea9999",
|
||||||
"#f9cb9c", "#f6b26b", "#e69138", "#b45f06", "#783f04",
|
"#e06666",
|
||||||
"#ffe599", "#ffd966", "#f1c232", "#bf9000", "#7f6000",
|
"#cc0000",
|
||||||
"#b6d7a8", "#93c47d", "#6aa84f", "#38761d", "#274e13",
|
"#990000",
|
||||||
"#a4c2f4", "#6d9eeb", "#3c78d8", "#1155cc", "#1c4587",
|
"#660000",
|
||||||
"#b4a7d6", "#8e7cc3", "#674ea7", "#351c75", "#20124d",
|
"#f9cb9c",
|
||||||
"#d5a6bd", "#c27ba0", "#a64d79", "#741b47", "#4c1130",
|
"#f6b26b",
|
||||||
"#ffffff", "#c0c0c0", "#838383", "#525252", "#000000",
|
"#e69138",
|
||||||
|
"#b45f06",
|
||||||
|
"#783f04",
|
||||||
|
"#ffe599",
|
||||||
|
"#ffd966",
|
||||||
|
"#f1c232",
|
||||||
|
"#bf9000",
|
||||||
|
"#7f6000",
|
||||||
|
"#b6d7a8",
|
||||||
|
"#93c47d",
|
||||||
|
"#6aa84f",
|
||||||
|
"#38761d",
|
||||||
|
"#274e13",
|
||||||
|
"#a4c2f4",
|
||||||
|
"#6d9eeb",
|
||||||
|
"#3c78d8",
|
||||||
|
"#1155cc",
|
||||||
|
"#1c4587",
|
||||||
|
"#b4a7d6",
|
||||||
|
"#8e7cc3",
|
||||||
|
"#674ea7",
|
||||||
|
"#351c75",
|
||||||
|
"#20124d",
|
||||||
|
"#d5a6bd",
|
||||||
|
"#c27ba0",
|
||||||
|
"#a64d79",
|
||||||
|
"#741b47",
|
||||||
|
"#4c1130",
|
||||||
|
"#ffffff",
|
||||||
|
"#c0c0c0",
|
||||||
|
"#838383",
|
||||||
|
"#525252",
|
||||||
|
"#000000"
|
||||||
],
|
],
|
||||||
initElement: (element, option) => {
|
initElement: (element, option) => {
|
||||||
if (option == "custom") {
|
if (option == "custom") {
|
||||||
@ -238,12 +270,11 @@ var IMAGE_EDITOR_SECTIONS = [
|
|||||||
input.click()
|
input.click()
|
||||||
}
|
}
|
||||||
element.appendChild(span)
|
element.appendChild(span)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
element.style.background = option
|
element.style.background = option
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getCustom: editor => {
|
getCustom: (editor) => {
|
||||||
var input = editor.popup.querySelector(".image_editor_color input")
|
var input = editor.popup.querySelector(".image_editor_color input")
|
||||||
return input.value
|
return input.value
|
||||||
}
|
}
|
||||||
@ -257,7 +288,7 @@ var IMAGE_EDITOR_SECTIONS = [
|
|||||||
element.parentElement.style.flex = option
|
element.parentElement.style.flex = option
|
||||||
element.style.width = option + "px"
|
element.style.width = option + "px"
|
||||||
element.style.height = option + "px"
|
element.style.height = option + "px"
|
||||||
element.style['margin-right'] = '2px'
|
element.style["margin-right"] = "2px"
|
||||||
element.style["border-radius"] = (option / 2).toFixed() + "px"
|
element.style["border-radius"] = (option / 2).toFixed() + "px"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -283,7 +314,7 @@ var IMAGE_EDITOR_SECTIONS = [
|
|||||||
sub_element.style.filter = `blur(${blur_amount}px)`
|
sub_element.style.filter = `blur(${blur_amount}px)`
|
||||||
sub_element.style.width = `${size - 2}px`
|
sub_element.style.width = `${size - 2}px`
|
||||||
sub_element.style.height = `${size - 2}px`
|
sub_element.style.height = `${size - 2}px`
|
||||||
sub_element.style['border-radius'] = `${size}px`
|
sub_element.style["border-radius"] = `${size}px`
|
||||||
element.style.background = "none"
|
element.style.background = "none"
|
||||||
element.appendChild(sub_element)
|
element.appendChild(sub_element)
|
||||||
}
|
}
|
||||||
@ -313,7 +344,7 @@ class EditorHistory {
|
|||||||
this.push({
|
this.push({
|
||||||
type: "action",
|
type: "action",
|
||||||
id: action
|
id: action
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
editBegin(x, y) {
|
editBegin(x, y) {
|
||||||
this.current_edit = {
|
this.current_edit = {
|
||||||
@ -345,7 +376,7 @@ class EditorHistory {
|
|||||||
}
|
}
|
||||||
rewindTo(new_rewind_index) {
|
rewindTo(new_rewind_index) {
|
||||||
if (new_rewind_index < 0 || new_rewind_index > this.events.length) {
|
if (new_rewind_index < 0 || new_rewind_index > this.events.length) {
|
||||||
return; // do nothing if target index is out of bounds
|
return // do nothing if target index is out of bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
var ctx = this.editor.layers.drawing.ctx
|
var ctx = this.editor.layers.drawing.ctx
|
||||||
@ -361,17 +392,16 @@ class EditorHistory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (snapshot_index != -1) {
|
if (snapshot_index != -1) {
|
||||||
ctx.putImageData(this.events[snapshot_index].snapshot, 0, 0);
|
ctx.putImageData(this.events[snapshot_index].snapshot, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = (snapshot_index + 1); i <= target_index; i++) {
|
for (var i = snapshot_index + 1; i <= target_index; i++) {
|
||||||
var event = this.events[i]
|
var event = this.events[i]
|
||||||
if (event.type == "action") {
|
if (event.type == "action") {
|
||||||
var action = IMAGE_EDITOR_ACTIONS.find(a => a.id == event.id)
|
var action = IMAGE_EDITOR_ACTIONS.find((a) => a.id == event.id)
|
||||||
action.handler(this.editor)
|
action.handler(this.editor)
|
||||||
}
|
} else if (event.type == "edit") {
|
||||||
else if (event.type == "edit") {
|
var tool = IMAGE_EDITOR_TOOLS.find((t) => t.id == event.id)
|
||||||
var tool = IMAGE_EDITOR_TOOLS.find(t => t.id == event.id)
|
|
||||||
this.editor.setBrush(this.editor.layers.drawing, event.options)
|
this.editor.setBrush(this.editor.layers.drawing, event.options)
|
||||||
|
|
||||||
var first_point = event.points[0]
|
var first_point = event.points[0]
|
||||||
@ -403,12 +433,8 @@ class ImageEditor {
|
|||||||
this.temp_previous_tool = null // used for the ctrl-colorpicker functionality
|
this.temp_previous_tool = null // used for the ctrl-colorpicker functionality
|
||||||
this.container = popup.querySelector(".editor-controls-center > div")
|
this.container = popup.querySelector(".editor-controls-center > div")
|
||||||
this.layers = {}
|
this.layers = {}
|
||||||
var layer_names = [
|
var layer_names = ["background", "drawing", "overlay"]
|
||||||
"background",
|
layer_names.forEach((name) => {
|
||||||
"drawing",
|
|
||||||
"overlay"
|
|
||||||
]
|
|
||||||
layer_names.forEach(name => {
|
|
||||||
let canvas = document.createElement("canvas")
|
let canvas = document.createElement("canvas")
|
||||||
canvas.className = `editor-canvas-${name}`
|
canvas.className = `editor-canvas-${name}`
|
||||||
this.container.appendChild(canvas)
|
this.container.appendChild(canvas)
|
||||||
@ -434,7 +460,7 @@ class ImageEditor {
|
|||||||
// initialize editor controls
|
// initialize editor controls
|
||||||
this.options = {}
|
this.options = {}
|
||||||
this.optionElements = {}
|
this.optionElements = {}
|
||||||
IMAGE_EDITOR_SECTIONS.forEach(section => {
|
IMAGE_EDITOR_SECTIONS.forEach((section) => {
|
||||||
section.id = `image_editor_${section.name}`
|
section.id = `image_editor_${section.name}`
|
||||||
var sectionElement = document.createElement("div")
|
var sectionElement = document.createElement("div")
|
||||||
sectionElement.className = section.id
|
sectionElement.className = section.id
|
||||||
@ -452,7 +478,7 @@ class ImageEditor {
|
|||||||
var optionElement = document.createElement("div")
|
var optionElement = document.createElement("div")
|
||||||
optionHolder.appendChild(optionElement)
|
optionHolder.appendChild(optionElement)
|
||||||
section.initElement(optionElement, option)
|
section.initElement(optionElement, option)
|
||||||
optionElement.addEventListener("click", target => this.selectOption(section.name, index))
|
optionElement.addEventListener("click", (target) => this.selectOption(section.name, index))
|
||||||
optionsContainer.appendChild(optionHolder)
|
optionsContainer.appendChild(optionHolder)
|
||||||
this.optionElements[section.name].push(optionElement)
|
this.optionElements[section.name].push(optionElement)
|
||||||
})
|
})
|
||||||
@ -470,13 +496,13 @@ class ImageEditor {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (this.inpainter) {
|
if (this.inpainter) {
|
||||||
this.selectOption("color", IMAGE_EDITOR_SECTIONS.find(s => s.name == "color").options.indexOf("#ffffff"))
|
this.selectOption("color", IMAGE_EDITOR_SECTIONS.find((s) => s.name == "color").options.indexOf("#ffffff"))
|
||||||
this.selectOption("opacity", IMAGE_EDITOR_SECTIONS.find(s => s.name == "opacity").options.indexOf(0.4))
|
this.selectOption("opacity", IMAGE_EDITOR_SECTIONS.find((s) => s.name == "opacity").options.indexOf(0.4))
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize the right-side controls
|
// initialize the right-side controls
|
||||||
var buttonContainer = document.createElement("div")
|
var buttonContainer = document.createElement("div")
|
||||||
IMAGE_EDITOR_BUTTONS.forEach(button => {
|
IMAGE_EDITOR_BUTTONS.forEach((button) => {
|
||||||
var element = document.createElement("div")
|
var element = document.createElement("div")
|
||||||
var icon = document.createElement("i")
|
var icon = document.createElement("i")
|
||||||
element.className = "image-editor-button button"
|
element.className = "image-editor-button button"
|
||||||
@ -484,13 +510,13 @@ class ImageEditor {
|
|||||||
element.appendChild(icon)
|
element.appendChild(icon)
|
||||||
element.append(button.name)
|
element.append(button.name)
|
||||||
buttonContainer.appendChild(element)
|
buttonContainer.appendChild(element)
|
||||||
element.addEventListener("click", event => button.handler(this))
|
element.addEventListener("click", (event) => button.handler(this))
|
||||||
})
|
})
|
||||||
var actionsContainer = document.createElement("div")
|
var actionsContainer = document.createElement("div")
|
||||||
var actionsTitle = document.createElement("h4")
|
var actionsTitle = document.createElement("h4")
|
||||||
actionsTitle.textContent = "Actions"
|
actionsTitle.textContent = "Actions"
|
||||||
actionsContainer.appendChild(actionsTitle);
|
actionsContainer.appendChild(actionsTitle)
|
||||||
IMAGE_EDITOR_ACTIONS.forEach(action => {
|
IMAGE_EDITOR_ACTIONS.forEach((action) => {
|
||||||
var element = document.createElement("div")
|
var element = document.createElement("div")
|
||||||
var icon = document.createElement("i")
|
var icon = document.createElement("i")
|
||||||
element.className = "image-editor-button button"
|
element.className = "image-editor-button button"
|
||||||
@ -501,7 +527,7 @@ class ImageEditor {
|
|||||||
element.appendChild(icon)
|
element.appendChild(icon)
|
||||||
element.append(action.name)
|
element.append(action.name)
|
||||||
actionsContainer.appendChild(element)
|
actionsContainer.appendChild(element)
|
||||||
element.addEventListener("click", event => this.runAction(action.id))
|
element.addEventListener("click", (event) => this.runAction(action.id))
|
||||||
})
|
})
|
||||||
this.popup.querySelector(".editor-controls-right").appendChild(actionsContainer)
|
this.popup.querySelector(".editor-controls-right").appendChild(actionsContainer)
|
||||||
this.popup.querySelector(".editor-controls-right").appendChild(buttonContainer)
|
this.popup.querySelector(".editor-controls-right").appendChild(buttonContainer)
|
||||||
@ -530,8 +556,7 @@ class ImageEditor {
|
|||||||
var multiplier = max_size / width
|
var multiplier = max_size / width
|
||||||
width = (multiplier * width).toFixed()
|
width = (multiplier * width).toFixed()
|
||||||
height = (multiplier * height).toFixed()
|
height = (multiplier * height).toFixed()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var max_size = Math.min(parseInt(window.innerHeight * 0.9), height, 768)
|
var max_size = Math.min(parseInt(window.innerHeight * 0.9), height, 768)
|
||||||
var multiplier = max_size / height
|
var multiplier = max_size / height
|
||||||
width = (multiplier * width).toFixed()
|
width = (multiplier * width).toFixed()
|
||||||
@ -543,7 +568,7 @@ class ImageEditor {
|
|||||||
this.container.style.width = width + "px"
|
this.container.style.width = width + "px"
|
||||||
this.container.style.height = height + "px"
|
this.container.style.height = height + "px"
|
||||||
|
|
||||||
Object.values(this.layers).forEach(layer => {
|
Object.values(this.layers).forEach((layer) => {
|
||||||
layer.canvas.width = width
|
layer.canvas.width = width
|
||||||
layer.canvas.height = height
|
layer.canvas.height = height
|
||||||
})
|
})
|
||||||
@ -556,11 +581,11 @@ class ImageEditor {
|
|||||||
}
|
}
|
||||||
get tool() {
|
get tool() {
|
||||||
var tool_id = this.getOptionValue("tool")
|
var tool_id = this.getOptionValue("tool")
|
||||||
return IMAGE_EDITOR_TOOLS.find(t => t.id == tool_id);
|
return IMAGE_EDITOR_TOOLS.find((t) => t.id == tool_id)
|
||||||
}
|
}
|
||||||
loadTool() {
|
loadTool() {
|
||||||
this.drawing = false
|
this.drawing = false
|
||||||
this.container.style.cursor = this.tool.cursor;
|
this.container.style.cursor = this.tool.cursor
|
||||||
}
|
}
|
||||||
setImage(url, width, height) {
|
setImage(url, width, height) {
|
||||||
this.setSize(width, height)
|
this.setSize(width, height)
|
||||||
@ -574,8 +599,7 @@ class ImageEditor {
|
|||||||
this.layers.background.ctx.drawImage(image, 0, 0, this.width, this.height)
|
this.layers.background.ctx.drawImage(image, 0, 0, this.width, this.height)
|
||||||
}
|
}
|
||||||
image.src = url
|
image.src = url
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.layers.background.ctx.fillStyle = "#ffffff"
|
this.layers.background.ctx.fillStyle = "#ffffff"
|
||||||
this.layers.background.ctx.beginPath()
|
this.layers.background.ctx.beginPath()
|
||||||
this.layers.background.ctx.rect(0, 0, this.width, this.height)
|
this.layers.background.ctx.rect(0, 0, this.width, this.height)
|
||||||
@ -589,23 +613,24 @@ class ImageEditor {
|
|||||||
this.layers.background.ctx.drawImage(this.layers.drawing.canvas, 0, 0, this.width, this.height)
|
this.layers.background.ctx.drawImage(this.layers.drawing.canvas, 0, 0, this.width, this.height)
|
||||||
var base64 = this.layers.background.canvas.toDataURL()
|
var base64 = this.layers.background.canvas.toDataURL()
|
||||||
initImagePreview.src = base64 // this will trigger the rest of the app to use it
|
initImagePreview.src = base64 // this will trigger the rest of the app to use it
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// This is an inpainter, so make sure the toggle is set accordingly
|
// This is an inpainter, so make sure the toggle is set accordingly
|
||||||
var is_blank = !this.layers.drawing.ctx
|
var is_blank = !this.layers.drawing.ctx
|
||||||
.getImageData(0, 0, this.width, this.height).data
|
.getImageData(0, 0, this.width, this.height)
|
||||||
.some(channel => channel !== 0)
|
.data.some((channel) => channel !== 0)
|
||||||
maskSetting.checked = !is_blank
|
maskSetting.checked = !is_blank
|
||||||
}
|
}
|
||||||
this.hide()
|
this.hide()
|
||||||
}
|
}
|
||||||
getImg() { // a drop-in replacement of the drawingboard version
|
getImg() {
|
||||||
|
// a drop-in replacement of the drawingboard version
|
||||||
return this.layers.drawing.canvas.toDataURL()
|
return this.layers.drawing.canvas.toDataURL()
|
||||||
}
|
}
|
||||||
setImg(dataUrl) { // a drop-in replacement of the drawingboard version
|
setImg(dataUrl) {
|
||||||
|
// a drop-in replacement of the drawingboard version
|
||||||
var image = new Image()
|
var image = new Image()
|
||||||
image.onload = () => {
|
image.onload = () => {
|
||||||
var ctx = this.layers.drawing.ctx;
|
var ctx = this.layers.drawing.ctx
|
||||||
ctx.clearRect(0, 0, this.width, this.height)
|
ctx.clearRect(0, 0, this.width, this.height)
|
||||||
ctx.globalCompositeOperation = "source-over"
|
ctx.globalCompositeOperation = "source-over"
|
||||||
ctx.globalAlpha = 1
|
ctx.globalAlpha = 1
|
||||||
@ -616,7 +641,7 @@ class ImageEditor {
|
|||||||
image.src = dataUrl
|
image.src = dataUrl
|
||||||
}
|
}
|
||||||
runAction(action_id) {
|
runAction(action_id) {
|
||||||
var action = IMAGE_EDITOR_ACTIONS.find(a => a.id == action_id)
|
var action = IMAGE_EDITOR_ACTIONS.find((a) => a.id == action_id)
|
||||||
if (action.trackHistory) {
|
if (action.trackHistory) {
|
||||||
this.history.pushAction(action_id)
|
this.history.pushAction(action_id)
|
||||||
}
|
}
|
||||||
@ -634,15 +659,16 @@ class ImageEditor {
|
|||||||
layer.ctx.strokeStyle = options.color
|
layer.ctx.strokeStyle = options.color
|
||||||
var sharpness = parseInt(options.sharpness * options.brush_size)
|
var sharpness = parseInt(options.sharpness * options.brush_size)
|
||||||
layer.ctx.filter = sharpness == 0 ? `none` : `blur(${sharpness}px)`
|
layer.ctx.filter = sharpness == 0 ? `none` : `blur(${sharpness}px)`
|
||||||
layer.ctx.globalAlpha = (1 - options.opacity)
|
layer.ctx.globalAlpha = 1 - options.opacity
|
||||||
layer.ctx.globalCompositeOperation = "source-over"
|
layer.ctx.globalCompositeOperation = "source-over"
|
||||||
var tool = IMAGE_EDITOR_TOOLS.find(t => t.id == options.tool)
|
var tool = IMAGE_EDITOR_TOOLS.find((t) => t.id == options.tool)
|
||||||
if (tool && tool.setBrush) {
|
if (tool && tool.setBrush) {
|
||||||
tool.setBrush(editor, layer)
|
tool.setBrush(editor, layer)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
Object.values(["drawing", "overlay"])
|
||||||
Object.values([ "drawing", "overlay" ]).map(name => this.layers[name]).forEach(l => {
|
.map((name) => this.layers[name])
|
||||||
|
.forEach((l) => {
|
||||||
this.setBrush(l)
|
this.setBrush(l)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -650,13 +676,15 @@ class ImageEditor {
|
|||||||
get ctx_overlay() {
|
get ctx_overlay() {
|
||||||
return this.layers.overlay.ctx
|
return this.layers.overlay.ctx
|
||||||
}
|
}
|
||||||
get ctx_current() { // the idea is this will help support having custom layers and editing each one
|
get ctx_current() {
|
||||||
|
// the idea is this will help support having custom layers and editing each one
|
||||||
return this.layers.drawing.ctx
|
return this.layers.drawing.ctx
|
||||||
}
|
}
|
||||||
get canvas_current() {
|
get canvas_current() {
|
||||||
return this.layers.drawing.canvas
|
return this.layers.drawing.canvas
|
||||||
}
|
}
|
||||||
keyHandler(event) { // handles keybinds like ctrl+z, ctrl+y
|
keyHandler(event) {
|
||||||
|
// handles keybinds like ctrl+z, ctrl+y
|
||||||
if (!this.popup.classList.contains("active")) {
|
if (!this.popup.classList.contains("active")) {
|
||||||
document.removeEventListener("keydown", this.keyHandlerBound)
|
document.removeEventListener("keydown", this.keyHandlerBound)
|
||||||
document.removeEventListener("keyup", this.keyHandlerBound)
|
document.removeEventListener("keyup", this.keyHandlerBound)
|
||||||
@ -668,41 +696,45 @@ class ImageEditor {
|
|||||||
if ((event.key == "z" || event.key == "Z") && event.ctrlKey) {
|
if ((event.key == "z" || event.key == "Z") && event.ctrlKey) {
|
||||||
if (!event.shiftKey) {
|
if (!event.shiftKey) {
|
||||||
this.history.undo()
|
this.history.undo()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.history.redo()
|
this.history.redo()
|
||||||
}
|
}
|
||||||
event.stopPropagation();
|
event.stopPropagation()
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
if (event.key == "y" && event.ctrlKey) {
|
if (event.key == "y" && event.ctrlKey) {
|
||||||
this.history.redo()
|
this.history.redo()
|
||||||
event.stopPropagation();
|
event.stopPropagation()
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
if (event.key === "Escape") {
|
if (event.key === "Escape") {
|
||||||
this.hide()
|
this.hide()
|
||||||
event.stopPropagation();
|
event.stopPropagation()
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dropper ctrl holding handler stuff
|
// dropper ctrl holding handler stuff
|
||||||
var dropper_active = this.temp_previous_tool != null;
|
var dropper_active = this.temp_previous_tool != null
|
||||||
if (dropper_active && !event.ctrlKey) {
|
if (dropper_active && !event.ctrlKey) {
|
||||||
this.selectOption("tool", IMAGE_EDITOR_TOOLS.findIndex(t => t.id == this.temp_previous_tool))
|
this.selectOption(
|
||||||
|
"tool",
|
||||||
|
IMAGE_EDITOR_TOOLS.findIndex((t) => t.id == this.temp_previous_tool)
|
||||||
|
)
|
||||||
this.temp_previous_tool = null
|
this.temp_previous_tool = null
|
||||||
}
|
} else if (!dropper_active && event.ctrlKey) {
|
||||||
else if (!dropper_active && event.ctrlKey) {
|
|
||||||
this.temp_previous_tool = this.getOptionValue("tool")
|
this.temp_previous_tool = this.getOptionValue("tool")
|
||||||
this.selectOption("tool", IMAGE_EDITOR_TOOLS.findIndex(t => t.id == "colorpicker"))
|
this.selectOption(
|
||||||
|
"tool",
|
||||||
|
IMAGE_EDITOR_TOOLS.findIndex((t) => t.id == "colorpicker")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mouseHandler(event) {
|
mouseHandler(event) {
|
||||||
var bbox = this.layers.overlay.canvas.getBoundingClientRect()
|
var bbox = this.layers.overlay.canvas.getBoundingClientRect()
|
||||||
var x = (event.clientX || 0) - bbox.left
|
var x = (event.clientX || 0) - bbox.left
|
||||||
var y = (event.clientY || 0) - bbox.top
|
var y = (event.clientY || 0) - bbox.top
|
||||||
var type = event.type;
|
var type = event.type
|
||||||
var touchmap = {
|
var touchmap = {
|
||||||
touchstart: "mousedown",
|
touchstart: "mousedown",
|
||||||
touchmove: "mousemove",
|
touchmove: "mousemove",
|
||||||
@ -744,15 +776,15 @@ class ImageEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
getOptionValue(section_name) {
|
getOptionValue(section_name) {
|
||||||
var section = IMAGE_EDITOR_SECTIONS.find(s => s.name == section_name)
|
var section = IMAGE_EDITOR_SECTIONS.find((s) => s.name == section_name)
|
||||||
return this.options && section_name in this.options ? this.options[section_name] : section.default
|
return this.options && section_name in this.options ? this.options[section_name] : section.default
|
||||||
}
|
}
|
||||||
selectOption(section_name, option_index) {
|
selectOption(section_name, option_index) {
|
||||||
var section = IMAGE_EDITOR_SECTIONS.find(s => s.name == section_name)
|
var section = IMAGE_EDITOR_SECTIONS.find((s) => s.name == section_name)
|
||||||
var value = section.options[option_index]
|
var value = section.options[option_index]
|
||||||
this.options[section_name] = value == "custom" ? section.getCustom(this) : value
|
this.options[section_name] = value == "custom" ? section.getCustom(this) : value
|
||||||
|
|
||||||
this.optionElements[section_name].forEach(element => element.classList.remove("active"))
|
this.optionElements[section_name].forEach((element) => element.classList.remove("active"))
|
||||||
this.optionElements[section_name][option_index].classList.add("active")
|
this.optionElements[section_name][option_index].classList.add("active")
|
||||||
|
|
||||||
// change the editor
|
// change the editor
|
||||||
@ -778,7 +810,6 @@ document.getElementById("init_image_button_inpaint").addEventListener("click", (
|
|||||||
|
|
||||||
img2imgUnload() // no init image when the app starts
|
img2imgUnload() // no init image when the app starts
|
||||||
|
|
||||||
|
|
||||||
function rgbToHex(rgb) {
|
function rgbToHex(rgb) {
|
||||||
function componentToHex(c) {
|
function componentToHex(c) {
|
||||||
var hex = parseInt(c).toString(16)
|
var hex = parseInt(c).toString(16)
|
||||||
@ -788,12 +819,14 @@ function rgbToHex(rgb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hexToRgb(hex) {
|
function hexToRgb(hex) {
|
||||||
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
||||||
return result ? {
|
return result
|
||||||
|
? {
|
||||||
r: parseInt(result[1], 16),
|
r: parseInt(result[1], 16),
|
||||||
g: parseInt(result[2], 16),
|
g: parseInt(result[2], 16),
|
||||||
b: parseInt(result[3], 16)
|
b: parseInt(result[3], 16)
|
||||||
} : null;
|
}
|
||||||
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function pixelCompare(int1, int2) {
|
function pixelCompare(int1, int2) {
|
||||||
@ -802,82 +835,93 @@ function pixelCompare(int1, int2) {
|
|||||||
|
|
||||||
// adapted from https://ben.akrin.com/canvas_fill/fill_04.html
|
// adapted from https://ben.akrin.com/canvas_fill/fill_04.html
|
||||||
function flood_fill(editor, the_canvas_context, x, y, color) {
|
function flood_fill(editor, the_canvas_context, x, y, color) {
|
||||||
pixel_stack = [{x:x, y:y}] ;
|
pixel_stack = [{ x: x, y: y }]
|
||||||
pixels = the_canvas_context.getImageData( 0, 0, editor.width, editor.height ) ;
|
pixels = the_canvas_context.getImageData(0, 0, editor.width, editor.height)
|
||||||
var linear_cords = ( y * editor.width + x ) * 4 ;
|
var linear_cords = (y * editor.width + x) * 4
|
||||||
var original_color = {r:pixels.data[linear_cords],
|
var original_color = {
|
||||||
|
r: pixels.data[linear_cords],
|
||||||
g: pixels.data[linear_cords + 1],
|
g: pixels.data[linear_cords + 1],
|
||||||
b: pixels.data[linear_cords + 2],
|
b: pixels.data[linear_cords + 2],
|
||||||
a:pixels.data[linear_cords+3]} ;
|
a: pixels.data[linear_cords + 3]
|
||||||
|
}
|
||||||
|
|
||||||
var opacity = color.a / 255;
|
var opacity = color.a / 255
|
||||||
var new_color = {
|
var new_color = {
|
||||||
r: parseInt((color.r * opacity) + (original_color.r * (1 - opacity))),
|
r: parseInt(color.r * opacity + original_color.r * (1 - opacity)),
|
||||||
g: parseInt((color.g * opacity) + (original_color.g * (1 - opacity))),
|
g: parseInt(color.g * opacity + original_color.g * (1 - opacity)),
|
||||||
b: parseInt((color.b * opacity) + (original_color.b * (1 - opacity)))
|
b: parseInt(color.b * opacity + original_color.b * (1 - opacity))
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pixelCompare(new_color.r, original_color.r) &&
|
if (
|
||||||
|
pixelCompare(new_color.r, original_color.r) &&
|
||||||
pixelCompare(new_color.g, original_color.g) &&
|
pixelCompare(new_color.g, original_color.g) &&
|
||||||
pixelCompare(new_color.b, original_color.b)))
|
pixelCompare(new_color.b, original_color.b)
|
||||||
{
|
) {
|
||||||
return; // This color is already the color we want, so do nothing
|
return // This color is already the color we want, so do nothing
|
||||||
}
|
}
|
||||||
var max_stack_size = editor.width * editor.height;
|
var max_stack_size = editor.width * editor.height
|
||||||
while (pixel_stack.length > 0 && pixel_stack.length < max_stack_size) {
|
while (pixel_stack.length > 0 && pixel_stack.length < max_stack_size) {
|
||||||
new_pixel = pixel_stack.shift() ;
|
new_pixel = pixel_stack.shift()
|
||||||
x = new_pixel.x ;
|
x = new_pixel.x
|
||||||
y = new_pixel.y ;
|
y = new_pixel.y
|
||||||
|
|
||||||
linear_cords = ( y * editor.width + x ) * 4 ;
|
linear_cords = (y * editor.width + x) * 4
|
||||||
while( y-->=0 &&
|
while (
|
||||||
(pixelCompare(pixels.data[linear_cords], original_color.r) &&
|
y-- >= 0 &&
|
||||||
|
pixelCompare(pixels.data[linear_cords], original_color.r) &&
|
||||||
pixelCompare(pixels.data[linear_cords + 1], original_color.g) &&
|
pixelCompare(pixels.data[linear_cords + 1], original_color.g) &&
|
||||||
pixelCompare(pixels.data[linear_cords+2], original_color.b))) {
|
pixelCompare(pixels.data[linear_cords + 2], original_color.b)
|
||||||
linear_cords -= editor.width * 4 ;
|
) {
|
||||||
|
linear_cords -= editor.width * 4
|
||||||
}
|
}
|
||||||
linear_cords += editor.width * 4 ;
|
linear_cords += editor.width * 4
|
||||||
y++ ;
|
y++
|
||||||
|
|
||||||
var reached_left = false ;
|
var reached_left = false
|
||||||
var reached_right = false ;
|
var reached_right = false
|
||||||
while( y++<editor.height &&
|
while (
|
||||||
(pixelCompare(pixels.data[linear_cords], original_color.r) &&
|
y++ < editor.height &&
|
||||||
|
pixelCompare(pixels.data[linear_cords], original_color.r) &&
|
||||||
pixelCompare(pixels.data[linear_cords + 1], original_color.g) &&
|
pixelCompare(pixels.data[linear_cords + 1], original_color.g) &&
|
||||||
pixelCompare(pixels.data[linear_cords+2], original_color.b))) {
|
pixelCompare(pixels.data[linear_cords + 2], original_color.b)
|
||||||
pixels.data[linear_cords] = new_color.r ;
|
) {
|
||||||
pixels.data[linear_cords+1] = new_color.g ;
|
pixels.data[linear_cords] = new_color.r
|
||||||
pixels.data[linear_cords+2] = new_color.b ;
|
pixels.data[linear_cords + 1] = new_color.g
|
||||||
pixels.data[linear_cords+3] = 255 ;
|
pixels.data[linear_cords + 2] = new_color.b
|
||||||
|
pixels.data[linear_cords + 3] = 255
|
||||||
|
|
||||||
if (x > 0) {
|
if (x > 0) {
|
||||||
if( pixelCompare(pixels.data[linear_cords-4], original_color.r) &&
|
if (
|
||||||
|
pixelCompare(pixels.data[linear_cords - 4], original_color.r) &&
|
||||||
pixelCompare(pixels.data[linear_cords - 4 + 1], original_color.g) &&
|
pixelCompare(pixels.data[linear_cords - 4 + 1], original_color.g) &&
|
||||||
pixelCompare(pixels.data[linear_cords-4+2], original_color.b)) {
|
pixelCompare(pixels.data[linear_cords - 4 + 2], original_color.b)
|
||||||
|
) {
|
||||||
if (!reached_left) {
|
if (!reached_left) {
|
||||||
pixel_stack.push( {x:x-1, y:y} ) ;
|
pixel_stack.push({ x: x - 1, y: y })
|
||||||
reached_left = true ;
|
reached_left = true
|
||||||
}
|
}
|
||||||
} else if (reached_left) {
|
} else if (reached_left) {
|
||||||
reached_left = false ;
|
reached_left = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x < editor.width - 1) {
|
if (x < editor.width - 1) {
|
||||||
if( pixelCompare(pixels.data[linear_cords+4], original_color.r) &&
|
if (
|
||||||
|
pixelCompare(pixels.data[linear_cords + 4], original_color.r) &&
|
||||||
pixelCompare(pixels.data[linear_cords + 4 + 1], original_color.g) &&
|
pixelCompare(pixels.data[linear_cords + 4 + 1], original_color.g) &&
|
||||||
pixelCompare(pixels.data[linear_cords+4+2], original_color.b)) {
|
pixelCompare(pixels.data[linear_cords + 4 + 2], original_color.b)
|
||||||
|
) {
|
||||||
if (!reached_right) {
|
if (!reached_right) {
|
||||||
pixel_stack.push( {x:x+1,y:y} ) ;
|
pixel_stack.push({ x: x + 1, y: y })
|
||||||
reached_right = true ;
|
reached_right = true
|
||||||
}
|
}
|
||||||
} else if (reached_right) {
|
} else if (reached_right) {
|
||||||
reached_right = false ;
|
reached_right = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
linear_cords += editor.width * 4 ;
|
linear_cords += editor.width * 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
the_canvas_context.putImageData( pixels, 0, 0 ) ;
|
the_canvas_context.putImageData(pixels, 0, 0)
|
||||||
}
|
}
|
||||||
|
@ -11,56 +11,35 @@
|
|||||||
* @type {(() => (string | ImageModalRequest) | string | ImageModalRequest) => {}}
|
* @type {(() => (string | ImageModalRequest) | string | ImageModalRequest) => {}}
|
||||||
*/
|
*/
|
||||||
const imageModal = (function() {
|
const imageModal = (function() {
|
||||||
const backElem = createElement(
|
const backElem = createElement("i", undefined, ["fa-solid", "fa-arrow-left", "tertiaryButton"])
|
||||||
'i',
|
|
||||||
undefined,
|
|
||||||
['fa-solid', 'fa-arrow-left', 'tertiaryButton'],
|
|
||||||
)
|
|
||||||
|
|
||||||
const forwardElem = createElement(
|
const forwardElem = createElement("i", undefined, ["fa-solid", "fa-arrow-right", "tertiaryButton"])
|
||||||
'i',
|
|
||||||
undefined,
|
|
||||||
['fa-solid', 'fa-arrow-right', 'tertiaryButton'],
|
|
||||||
)
|
|
||||||
|
|
||||||
const zoomElem = createElement(
|
const zoomElem = createElement("i", undefined, ["fa-solid", "tertiaryButton"])
|
||||||
'i',
|
|
||||||
undefined,
|
|
||||||
['fa-solid', 'tertiaryButton'],
|
|
||||||
)
|
|
||||||
|
|
||||||
const closeElem = createElement(
|
const closeElem = createElement("i", undefined, ["fa-solid", "fa-xmark", "tertiaryButton"])
|
||||||
'i',
|
|
||||||
undefined,
|
|
||||||
['fa-solid', 'fa-xmark', 'tertiaryButton'],
|
|
||||||
)
|
|
||||||
|
|
||||||
const menuBarElem = createElement('div', undefined, 'menu-bar', [backElem, forwardElem, zoomElem, closeElem])
|
const menuBarElem = createElement("div", undefined, "menu-bar", [backElem, forwardElem, zoomElem, closeElem])
|
||||||
|
|
||||||
const imageContainer = createElement('div', undefined, 'image-wrapper')
|
const imageContainer = createElement("div", undefined, "image-wrapper")
|
||||||
|
|
||||||
const backdrop = createElement('div', undefined, 'backdrop')
|
const backdrop = createElement("div", undefined, "backdrop")
|
||||||
|
|
||||||
const modalContainer = createElement('div', undefined, 'content', [menuBarElem, imageContainer])
|
const modalContainer = createElement("div", undefined, "content", [menuBarElem, imageContainer])
|
||||||
|
|
||||||
const modalElem = createElement(
|
const modalElem = createElement("div", { id: "viewFullSizeImgModal" }, ["popup"], [backdrop, modalContainer])
|
||||||
'div',
|
|
||||||
{ id: 'viewFullSizeImgModal' },
|
|
||||||
['popup'],
|
|
||||||
[backdrop, modalContainer],
|
|
||||||
)
|
|
||||||
document.body.appendChild(modalElem)
|
document.body.appendChild(modalElem)
|
||||||
|
|
||||||
const setZoomLevel = (value) => {
|
const setZoomLevel = (value) => {
|
||||||
const img = imageContainer.querySelector('img')
|
const img = imageContainer.querySelector("img")
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
zoomElem.classList.remove('fa-magnifying-glass-plus')
|
zoomElem.classList.remove("fa-magnifying-glass-plus")
|
||||||
zoomElem.classList.add('fa-magnifying-glass-minus')
|
zoomElem.classList.add("fa-magnifying-glass-minus")
|
||||||
if (img) {
|
if (img) {
|
||||||
img.classList.remove('natural-zoom')
|
img.classList.remove("natural-zoom")
|
||||||
|
|
||||||
let zoomLevel = typeof value === 'number' ? value : img.dataset.zoomLevel
|
let zoomLevel = typeof value === "number" ? value : img.dataset.zoomLevel
|
||||||
if (!zoomLevel) {
|
if (!zoomLevel) {
|
||||||
zoomLevel = 100
|
zoomLevel = 100
|
||||||
}
|
}
|
||||||
@ -70,36 +49,35 @@ const imageModal = (function() {
|
|||||||
img.height = img.naturalHeight * (+zoomLevel / 100)
|
img.height = img.naturalHeight * (+zoomLevel / 100)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
zoomElem.classList.remove('fa-magnifying-glass-minus')
|
zoomElem.classList.remove("fa-magnifying-glass-minus")
|
||||||
zoomElem.classList.add('fa-magnifying-glass-plus')
|
zoomElem.classList.add("fa-magnifying-glass-plus")
|
||||||
if (img) {
|
if (img) {
|
||||||
img.classList.add('natural-zoom')
|
img.classList.add("natural-zoom")
|
||||||
img.removeAttribute('width')
|
img.removeAttribute("width")
|
||||||
img.removeAttribute('height')
|
img.removeAttribute("height")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
zoomElem.addEventListener(
|
zoomElem.addEventListener("click", () =>
|
||||||
'click',
|
setZoomLevel(imageContainer.querySelector("img")?.classList?.contains("natural-zoom"))
|
||||||
() => setZoomLevel(imageContainer.querySelector('img')?.classList?.contains('natural-zoom')),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
previous: undefined,
|
previous: undefined,
|
||||||
next: undefined,
|
next: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const clear = () => {
|
const clear = () => {
|
||||||
imageContainer.innerHTML = ''
|
imageContainer.innerHTML = ""
|
||||||
|
|
||||||
Object.keys(state).forEach(key => delete state[key])
|
Object.keys(state).forEach((key) => delete state[key])
|
||||||
}
|
}
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
clear()
|
clear()
|
||||||
modalElem.classList.remove('active')
|
modalElem.classList.remove("active")
|
||||||
document.body.style.overflow = 'initial'
|
document.body.style.overflow = "initial"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,27 +91,27 @@ const imageModal = (function() {
|
|||||||
|
|
||||||
clear()
|
clear()
|
||||||
|
|
||||||
const options = typeof optionsFactory === 'function' ? optionsFactory() : optionsFactory
|
const options = typeof optionsFactory === "function" ? optionsFactory() : optionsFactory
|
||||||
const src = typeof options === 'string' ? options : options.src
|
const src = typeof options === "string" ? options : options.src
|
||||||
|
|
||||||
const imgElem = createElement('img', { src }, 'natural-zoom')
|
const imgElem = createElement("img", { src }, "natural-zoom")
|
||||||
imageContainer.appendChild(imgElem)
|
imageContainer.appendChild(imgElem)
|
||||||
modalElem.classList.add('active')
|
modalElem.classList.add("active")
|
||||||
document.body.style.overflow = 'hidden'
|
document.body.style.overflow = "hidden"
|
||||||
setZoomLevel(false)
|
setZoomLevel(false)
|
||||||
|
|
||||||
if (typeof options === 'object' && options.previous) {
|
if (typeof options === "object" && options.previous) {
|
||||||
state.previous = options.previous
|
state.previous = options.previous
|
||||||
backElem.style.display = 'unset'
|
backElem.style.display = "unset"
|
||||||
} else {
|
} else {
|
||||||
backElem.style.display = 'none'
|
backElem.style.display = "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof options === 'object' && options.next) {
|
if (typeof options === "object" && options.next) {
|
||||||
state.next = options.next
|
state.next = options.next
|
||||||
forwardElem.style.display = 'unset'
|
forwardElem.style.display = "unset"
|
||||||
} else {
|
} else {
|
||||||
forwardElem.style.display = 'none'
|
forwardElem.style.display = "none"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +119,7 @@ const imageModal = (function() {
|
|||||||
if (state.previous) {
|
if (state.previous) {
|
||||||
init(state.previous)
|
init(state.previous)
|
||||||
} else {
|
} else {
|
||||||
backElem.style.display = 'none'
|
backElem.style.display = "none"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,27 +127,27 @@ const imageModal = (function() {
|
|||||||
if (state.next) {
|
if (state.next) {
|
||||||
init(state.next)
|
init(state.next)
|
||||||
} else {
|
} else {
|
||||||
forwardElem.style.display = 'none'
|
forwardElem.style.display = "none"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('keydown', (e) => {
|
window.addEventListener("keydown", (e) => {
|
||||||
if (modalElem.classList.contains('active')) {
|
if (modalElem.classList.contains("active")) {
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case 'Escape':
|
case "Escape":
|
||||||
close()
|
close()
|
||||||
break
|
break
|
||||||
case 'ArrowLeft':
|
case "ArrowLeft":
|
||||||
back()
|
back()
|
||||||
break
|
break
|
||||||
case 'ArrowRight':
|
case "ArrowRight":
|
||||||
forward()
|
forward()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
window.addEventListener('click', (e) => {
|
window.addEventListener("click", (e) => {
|
||||||
if (modalElem.classList.contains('active')) {
|
if (modalElem.classList.contains("active")) {
|
||||||
if (e.target === backdrop || e.target === closeElem) {
|
if (e.target === backdrop || e.target === closeElem) {
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
@ -180,9 +158,9 @@ const imageModal = (function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
backElem.addEventListener('click', back)
|
backElem.addEventListener("click", back)
|
||||||
|
|
||||||
forwardElem.addEventListener('click', forward)
|
forwardElem.addEventListener("click", forward)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {() => (string | ImageModalRequest) | string | ImageModalRequest} optionsFactory
|
* @param {() => (string | ImageModalRequest) | string | ImageModalRequest} optionsFactory
|
||||||
|
@ -3,26 +3,26 @@ let modifiers = []
|
|||||||
let customModifiersGroupElement = undefined
|
let customModifiersGroupElement = undefined
|
||||||
let customModifiersInitialContent
|
let customModifiersInitialContent
|
||||||
|
|
||||||
let editorModifierEntries = document.querySelector('#editor-modifiers-entries')
|
let editorModifierEntries = document.querySelector("#editor-modifiers-entries")
|
||||||
let editorModifierTagsList = document.querySelector('#editor-inputs-tags-list')
|
let editorModifierTagsList = document.querySelector("#editor-inputs-tags-list")
|
||||||
let editorTagsContainer = document.querySelector('#editor-inputs-tags-container')
|
let editorTagsContainer = document.querySelector("#editor-inputs-tags-container")
|
||||||
let modifierCardSizeSlider = document.querySelector('#modifier-card-size-slider')
|
let modifierCardSizeSlider = document.querySelector("#modifier-card-size-slider")
|
||||||
let previewImageField = document.querySelector('#preview-image')
|
let previewImageField = document.querySelector("#preview-image")
|
||||||
let modifierSettingsBtn = document.querySelector('#modifier-settings-btn')
|
let modifierSettingsBtn = document.querySelector("#modifier-settings-btn")
|
||||||
let modifierSettingsOverlay = document.querySelector('#modifier-settings-config')
|
let modifierSettingsOverlay = document.querySelector("#modifier-settings-config")
|
||||||
let customModifiersTextBox = document.querySelector('#custom-modifiers-input')
|
let customModifiersTextBox = document.querySelector("#custom-modifiers-input")
|
||||||
let customModifierEntriesToolbar = document.querySelector('#editor-modifiers-entries-toolbar')
|
let customModifierEntriesToolbar = document.querySelector("#editor-modifiers-entries-toolbar")
|
||||||
|
|
||||||
const modifierThumbnailPath = 'media/modifier-thumbnails'
|
const modifierThumbnailPath = "media/modifier-thumbnails"
|
||||||
const activeCardClass = 'modifier-card-active'
|
const activeCardClass = "modifier-card-active"
|
||||||
const CUSTOM_MODIFIERS_KEY = "customModifiers"
|
const CUSTOM_MODIFIERS_KEY = "customModifiers"
|
||||||
|
|
||||||
function createModifierCard(name, previews, removeBy) {
|
function createModifierCard(name, previews, removeBy) {
|
||||||
const modifierCard = document.createElement('div')
|
const modifierCard = document.createElement("div")
|
||||||
let style = previewImageField.value
|
let style = previewImageField.value
|
||||||
let styleIndex = (style=='portrait') ? 0 : 1
|
let styleIndex = style == "portrait" ? 0 : 1
|
||||||
|
|
||||||
modifierCard.className = 'modifier-card'
|
modifierCard.className = "modifier-card"
|
||||||
modifierCard.innerHTML = `
|
modifierCard.innerHTML = `
|
||||||
<div class="modifier-card-overlay"></div>
|
<div class="modifier-card-overlay"></div>
|
||||||
<div class="modifier-card-image-container">
|
<div class="modifier-card-image-container">
|
||||||
@ -34,35 +34,35 @@ function createModifierCard(name, previews, removeBy) {
|
|||||||
<div class="modifier-card-label"><p></p></div>
|
<div class="modifier-card-label"><p></p></div>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
const image = modifierCard.querySelector('.modifier-card-image')
|
const image = modifierCard.querySelector(".modifier-card-image")
|
||||||
const errorText = modifierCard.querySelector('.modifier-card-error-label')
|
const errorText = modifierCard.querySelector(".modifier-card-error-label")
|
||||||
const label = modifierCard.querySelector('.modifier-card-label')
|
const label = modifierCard.querySelector(".modifier-card-label")
|
||||||
|
|
||||||
errorText.innerText = 'No Image'
|
errorText.innerText = "No Image"
|
||||||
|
|
||||||
if (typeof previews == 'object') {
|
if (typeof previews == "object") {
|
||||||
image.src = previews[styleIndex]; // portrait
|
image.src = previews[styleIndex] // portrait
|
||||||
image.setAttribute('preview-type', style)
|
image.setAttribute("preview-type", style)
|
||||||
} else {
|
} else {
|
||||||
image.remove()
|
image.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxLabelLength = 30
|
const maxLabelLength = 30
|
||||||
const cardLabel = removeBy ? name.replace('by ', '') : name
|
const cardLabel = removeBy ? name.replace("by ", "") : name
|
||||||
|
|
||||||
if (cardLabel.length <= maxLabelLength) {
|
if (cardLabel.length <= maxLabelLength) {
|
||||||
label.querySelector('p').innerText = cardLabel
|
label.querySelector("p").innerText = cardLabel
|
||||||
} else {
|
} else {
|
||||||
const tooltipText = document.createElement('span')
|
const tooltipText = document.createElement("span")
|
||||||
tooltipText.className = 'tooltip-text'
|
tooltipText.className = "tooltip-text"
|
||||||
tooltipText.innerText = name
|
tooltipText.innerText = name
|
||||||
|
|
||||||
label.classList.add('tooltip')
|
label.classList.add("tooltip")
|
||||||
label.appendChild(tooltipText)
|
label.appendChild(tooltipText)
|
||||||
|
|
||||||
label.querySelector('p').innerText = cardLabel.substring(0, maxLabelLength) + '...'
|
label.querySelector("p").innerText = cardLabel.substring(0, maxLabelLength) + "..."
|
||||||
}
|
}
|
||||||
label.querySelector('p').dataset.fullName = name // preserve the full name
|
label.querySelector("p").dataset.fullName = name // preserve the full name
|
||||||
|
|
||||||
return modifierCard
|
return modifierCard
|
||||||
}
|
}
|
||||||
@ -71,55 +71,58 @@ function createModifierGroup(modifierGroup, initiallyExpanded, removeBy) {
|
|||||||
const title = modifierGroup.category
|
const title = modifierGroup.category
|
||||||
const modifiers = modifierGroup.modifiers
|
const modifiers = modifierGroup.modifiers
|
||||||
|
|
||||||
const titleEl = document.createElement('h5')
|
const titleEl = document.createElement("h5")
|
||||||
titleEl.className = 'collapsible'
|
titleEl.className = "collapsible"
|
||||||
titleEl.innerText = title
|
titleEl.innerText = title
|
||||||
|
|
||||||
const modifiersEl = document.createElement('div')
|
const modifiersEl = document.createElement("div")
|
||||||
modifiersEl.classList.add('collapsible-content', 'editor-modifiers-leaf')
|
modifiersEl.classList.add("collapsible-content", "editor-modifiers-leaf")
|
||||||
|
|
||||||
if (initiallyExpanded === true) {
|
if (initiallyExpanded === true) {
|
||||||
titleEl.className += ' active'
|
titleEl.className += " active"
|
||||||
}
|
}
|
||||||
|
|
||||||
modifiers.forEach(modObj => {
|
modifiers.forEach((modObj) => {
|
||||||
const modifierName = modObj.modifier
|
const modifierName = modObj.modifier
|
||||||
const modifierPreviews = modObj?.previews?.map(preview => `${IMAGE_REGEX.test(preview.image) ? preview.image : modifierThumbnailPath + '/' + preview.path}`)
|
const modifierPreviews = modObj?.previews?.map(
|
||||||
|
(preview) =>
|
||||||
|
`${IMAGE_REGEX.test(preview.image) ? preview.image : modifierThumbnailPath + "/" + preview.path}`
|
||||||
|
)
|
||||||
|
|
||||||
const modifierCard = createModifierCard(modifierName, modifierPreviews, removeBy)
|
const modifierCard = createModifierCard(modifierName, modifierPreviews, removeBy)
|
||||||
|
|
||||||
if(typeof modifierCard == 'object') {
|
if (typeof modifierCard == "object") {
|
||||||
modifiersEl.appendChild(modifierCard)
|
modifiersEl.appendChild(modifierCard)
|
||||||
const trimmedName = trimModifiers(modifierName)
|
const trimmedName = trimModifiers(modifierName)
|
||||||
|
|
||||||
modifierCard.addEventListener('click', () => {
|
modifierCard.addEventListener("click", () => {
|
||||||
if (activeTags.map(x => trimModifiers(x.name)).includes(trimmedName)) {
|
if (activeTags.map((x) => trimModifiers(x.name)).includes(trimmedName)) {
|
||||||
// remove modifier from active array
|
// remove modifier from active array
|
||||||
activeTags = activeTags.filter(x => trimModifiers(x.name) != trimmedName)
|
activeTags = activeTags.filter((x) => trimModifiers(x.name) != trimmedName)
|
||||||
toggleCardState(trimmedName, false)
|
toggleCardState(trimmedName, false)
|
||||||
} else {
|
} else {
|
||||||
// add modifier to active array
|
// add modifier to active array
|
||||||
activeTags.push({
|
activeTags.push({
|
||||||
'name': modifierName,
|
name: modifierName,
|
||||||
'element': modifierCard.cloneNode(true),
|
element: modifierCard.cloneNode(true),
|
||||||
'originElement': modifierCard,
|
originElement: modifierCard,
|
||||||
'previews': modifierPreviews
|
previews: modifierPreviews
|
||||||
})
|
})
|
||||||
toggleCardState(trimmedName, true)
|
toggleCardState(trimmedName, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshTagsList()
|
refreshTagsList()
|
||||||
document.dispatchEvent(new Event('refreshImageModifiers'))
|
document.dispatchEvent(new Event("refreshImageModifiers"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let brk = document.createElement('br')
|
let brk = document.createElement("br")
|
||||||
brk.style.clear = 'both'
|
brk.style.clear = "both"
|
||||||
modifiersEl.appendChild(brk)
|
modifiersEl.appendChild(brk)
|
||||||
|
|
||||||
let e = document.createElement('div')
|
let e = document.createElement("div")
|
||||||
e.className = 'modifier-category'
|
e.className = "modifier-category"
|
||||||
e.appendChild(titleEl)
|
e.appendChild(titleEl)
|
||||||
e.appendChild(modifiersEl)
|
e.appendChild(modifiersEl)
|
||||||
|
|
||||||
@ -130,87 +133,98 @@ function createModifierGroup(modifierGroup, initiallyExpanded, removeBy) {
|
|||||||
|
|
||||||
function trimModifiers(tag) {
|
function trimModifiers(tag) {
|
||||||
// Remove trailing '-' and/or '+'
|
// Remove trailing '-' and/or '+'
|
||||||
tag = tag.replace(/[-+]+$/, '');
|
tag = tag.replace(/[-+]+$/, "")
|
||||||
// Remove parentheses at beginning and end
|
// Remove parentheses at beginning and end
|
||||||
return tag.replace(/^[(]+|[\s)]+$/g, '');
|
return tag.replace(/^[(]+|[\s)]+$/g, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadModifiers() {
|
async function loadModifiers() {
|
||||||
try {
|
try {
|
||||||
let res = await fetch('/get/modifiers')
|
let res = await fetch("/get/modifiers")
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
res = await res.json()
|
res = await res.json()
|
||||||
|
|
||||||
modifiers = res; // update global variable
|
modifiers = res // update global variable
|
||||||
|
|
||||||
res.reverse()
|
res.reverse()
|
||||||
|
|
||||||
res.forEach((modifierGroup, idx) => {
|
res.forEach((modifierGroup, idx) => {
|
||||||
createModifierGroup(modifierGroup, idx === res.length - 1, modifierGroup === 'Artist' ? true : false) // only remove "By " for artists
|
createModifierGroup(modifierGroup, idx === res.length - 1, modifierGroup === "Artist" ? true : false) // only remove "By " for artists
|
||||||
})
|
})
|
||||||
|
|
||||||
createCollapsibles(editorModifierEntries)
|
createCollapsibles(editorModifierEntries)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('error fetching modifiers', e)
|
console.error("error fetching modifiers", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadCustomModifiers()
|
loadCustomModifiers()
|
||||||
resizeModifierCards(modifierCardSizeSlider.value)
|
resizeModifierCards(modifierCardSizeSlider.value)
|
||||||
document.dispatchEvent(new Event('loadImageModifiers'))
|
document.dispatchEvent(new Event("loadImageModifiers"))
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshModifiersState(newTags, inactiveTags) {
|
function refreshModifiersState(newTags, inactiveTags) {
|
||||||
// clear existing modifiers
|
// clear existing modifiers
|
||||||
document.querySelector('#editor-modifiers').querySelectorAll('.modifier-card').forEach(modifierCard => {
|
document
|
||||||
const modifierName = modifierCard.querySelector('.modifier-card-label p').dataset.fullName // pick the full modifier name
|
.querySelector("#editor-modifiers")
|
||||||
if (activeTags.map(x => x.name).includes(modifierName)) {
|
.querySelectorAll(".modifier-card")
|
||||||
|
.forEach((modifierCard) => {
|
||||||
|
const modifierName = modifierCard.querySelector(".modifier-card-label p").dataset.fullName // pick the full modifier name
|
||||||
|
if (activeTags.map((x) => x.name).includes(modifierName)) {
|
||||||
modifierCard.classList.remove(activeCardClass)
|
modifierCard.classList.remove(activeCardClass)
|
||||||
modifierCard.querySelector('.modifier-card-image-overlay').innerText = '+'
|
modifierCard.querySelector(".modifier-card-image-overlay").innerText = "+"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
activeTags = []
|
activeTags = []
|
||||||
|
|
||||||
// set new modifiers
|
// set new modifiers
|
||||||
newTags.forEach(tag => {
|
newTags.forEach((tag) => {
|
||||||
let found = false
|
let found = false
|
||||||
document.querySelector('#editor-modifiers').querySelectorAll('.modifier-card').forEach(modifierCard => {
|
document
|
||||||
const modifierName = modifierCard.querySelector('.modifier-card-label p').dataset.fullName
|
.querySelector("#editor-modifiers")
|
||||||
const shortModifierName = modifierCard.querySelector('.modifier-card-label p').innerText
|
.querySelectorAll(".modifier-card")
|
||||||
|
.forEach((modifierCard) => {
|
||||||
|
const modifierName = modifierCard.querySelector(".modifier-card-label p").dataset.fullName
|
||||||
|
const shortModifierName = modifierCard.querySelector(".modifier-card-label p").innerText
|
||||||
if (trimModifiers(tag) == trimModifiers(modifierName)) {
|
if (trimModifiers(tag) == trimModifiers(modifierName)) {
|
||||||
// add modifier to active array
|
// add modifier to active array
|
||||||
if (!activeTags.map(x => x.name).includes(tag)) { // only add each tag once even if several custom modifier cards share the same tag
|
if (!activeTags.map((x) => x.name).includes(tag)) {
|
||||||
|
// only add each tag once even if several custom modifier cards share the same tag
|
||||||
const imageModifierCard = modifierCard.cloneNode(true)
|
const imageModifierCard = modifierCard.cloneNode(true)
|
||||||
imageModifierCard.querySelector('.modifier-card-label p').innerText = tag.replace(modifierName, shortModifierName)
|
imageModifierCard.querySelector(".modifier-card-label p").innerText = tag.replace(
|
||||||
|
modifierName,
|
||||||
|
shortModifierName
|
||||||
|
)
|
||||||
activeTags.push({
|
activeTags.push({
|
||||||
'name': tag,
|
name: tag,
|
||||||
'element': imageModifierCard,
|
element: imageModifierCard,
|
||||||
'originElement': modifierCard
|
originElement: modifierCard
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
modifierCard.classList.add(activeCardClass)
|
modifierCard.classList.add(activeCardClass)
|
||||||
modifierCard.querySelector('.modifier-card-image-overlay').innerText = '-'
|
modifierCard.querySelector(".modifier-card-image-overlay").innerText = "-"
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (found == false) { // custom tag went missing, create one here
|
if (found == false) {
|
||||||
|
// custom tag went missing, create one here
|
||||||
let modifierCard = createModifierCard(tag, undefined, false) // create a modifier card for the missing tag, no image
|
let modifierCard = createModifierCard(tag, undefined, false) // create a modifier card for the missing tag, no image
|
||||||
|
|
||||||
modifierCard.addEventListener('click', () => {
|
modifierCard.addEventListener("click", () => {
|
||||||
if (activeTags.map(x => x.name).includes(tag)) {
|
if (activeTags.map((x) => x.name).includes(tag)) {
|
||||||
// remove modifier from active array
|
// remove modifier from active array
|
||||||
activeTags = activeTags.filter(x => x.name != tag)
|
activeTags = activeTags.filter((x) => x.name != tag)
|
||||||
modifierCard.classList.remove(activeCardClass)
|
modifierCard.classList.remove(activeCardClass)
|
||||||
|
|
||||||
modifierCard.querySelector('.modifier-card-image-overlay').innerText = '+'
|
modifierCard.querySelector(".modifier-card-image-overlay").innerText = "+"
|
||||||
}
|
}
|
||||||
refreshTagsList()
|
refreshTagsList()
|
||||||
})
|
})
|
||||||
|
|
||||||
activeTags.push({
|
activeTags.push({
|
||||||
'name': tag,
|
name: tag,
|
||||||
'element': modifierCard,
|
element: modifierCard,
|
||||||
'originElement': undefined // no origin element for missing tags
|
originElement: undefined // no origin element for missing tags
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -220,41 +234,44 @@ function refreshModifiersState(newTags, inactiveTags) {
|
|||||||
function refreshInactiveTags(inactiveTags) {
|
function refreshInactiveTags(inactiveTags) {
|
||||||
// update inactive tags
|
// update inactive tags
|
||||||
if (inactiveTags !== undefined && inactiveTags.length > 0) {
|
if (inactiveTags !== undefined && inactiveTags.length > 0) {
|
||||||
activeTags.forEach (tag => {
|
activeTags.forEach((tag) => {
|
||||||
if (inactiveTags.find(element => element === tag.name) !== undefined) {
|
if (inactiveTags.find((element) => element === tag.name) !== undefined) {
|
||||||
tag.inactive = true
|
tag.inactive = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// update cards
|
// update cards
|
||||||
let overlays = document.querySelector('#editor-inputs-tags-list').querySelectorAll('.modifier-card-overlay')
|
let overlays = document.querySelector("#editor-inputs-tags-list").querySelectorAll(".modifier-card-overlay")
|
||||||
overlays.forEach (i => {
|
overlays.forEach((i) => {
|
||||||
let modifierName = i.parentElement.getElementsByClassName('modifier-card-label')[0].getElementsByTagName("p")[0].dataset.fullName
|
let modifierName = i.parentElement.getElementsByClassName("modifier-card-label")[0].getElementsByTagName("p")[0]
|
||||||
if (inactiveTags?.find(element => element === modifierName) !== undefined) {
|
.dataset.fullName
|
||||||
i.parentElement.classList.add('modifier-toggle-inactive')
|
if (inactiveTags?.find((element) => element === modifierName) !== undefined) {
|
||||||
|
i.parentElement.classList.add("modifier-toggle-inactive")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshTagsList(inactiveTags) {
|
function refreshTagsList(inactiveTags) {
|
||||||
editorModifierTagsList.innerHTML = ''
|
editorModifierTagsList.innerHTML = ""
|
||||||
|
|
||||||
if (activeTags.length == 0) {
|
if (activeTags.length == 0) {
|
||||||
editorTagsContainer.style.display = 'none'
|
editorTagsContainer.style.display = "none"
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
editorTagsContainer.style.display = 'block'
|
editorTagsContainer.style.display = "block"
|
||||||
}
|
}
|
||||||
|
|
||||||
activeTags.forEach((tag, index) => {
|
activeTags.forEach((tag, index) => {
|
||||||
tag.element.querySelector('.modifier-card-image-overlay').innerText = '-'
|
tag.element.querySelector(".modifier-card-image-overlay").innerText = "-"
|
||||||
tag.element.classList.add('modifier-card-tiny')
|
tag.element.classList.add("modifier-card-tiny")
|
||||||
|
|
||||||
editorModifierTagsList.appendChild(tag.element)
|
editorModifierTagsList.appendChild(tag.element)
|
||||||
|
|
||||||
tag.element.addEventListener('click', () => {
|
tag.element.addEventListener("click", () => {
|
||||||
let idx = activeTags.findIndex(o => { return o.name === tag.name })
|
let idx = activeTags.findIndex((o) => {
|
||||||
|
return o.name === tag.name
|
||||||
|
})
|
||||||
|
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
toggleCardState(activeTags[idx].name, false)
|
toggleCardState(activeTags[idx].name, false)
|
||||||
@ -262,86 +279,89 @@ function refreshTagsList(inactiveTags) {
|
|||||||
activeTags.splice(idx, 1)
|
activeTags.splice(idx, 1)
|
||||||
refreshTagsList()
|
refreshTagsList()
|
||||||
}
|
}
|
||||||
document.dispatchEvent(new Event('refreshImageModifiers'))
|
document.dispatchEvent(new Event("refreshImageModifiers"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
let brk = document.createElement('br')
|
let brk = document.createElement("br")
|
||||||
brk.style.clear = 'both'
|
brk.style.clear = "both"
|
||||||
editorModifierTagsList.appendChild(brk)
|
editorModifierTagsList.appendChild(brk)
|
||||||
refreshInactiveTags(inactiveTags)
|
refreshInactiveTags(inactiveTags)
|
||||||
document.dispatchEvent(new Event('refreshImageModifiers')) // notify plugins that the image tags have been refreshed
|
document.dispatchEvent(new Event("refreshImageModifiers")) // notify plugins that the image tags have been refreshed
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleCardState(modifierName, makeActive) {
|
function toggleCardState(modifierName, makeActive) {
|
||||||
document.querySelector('#editor-modifiers').querySelectorAll('.modifier-card').forEach(card => {
|
document
|
||||||
const name = card.querySelector('.modifier-card-label').innerText
|
.querySelector("#editor-modifiers")
|
||||||
if ( trimModifiers(modifierName) == trimModifiers(name)
|
.querySelectorAll(".modifier-card")
|
||||||
|| trimModifiers(modifierName) == 'by ' + trimModifiers(name)) {
|
.forEach((card) => {
|
||||||
|
const name = card.querySelector(".modifier-card-label").innerText
|
||||||
|
if (
|
||||||
|
trimModifiers(modifierName) == trimModifiers(name) ||
|
||||||
|
trimModifiers(modifierName) == "by " + trimModifiers(name)
|
||||||
|
) {
|
||||||
if (makeActive) {
|
if (makeActive) {
|
||||||
card.classList.add(activeCardClass)
|
card.classList.add(activeCardClass)
|
||||||
card.querySelector('.modifier-card-image-overlay').innerText = '-'
|
card.querySelector(".modifier-card-image-overlay").innerText = "-"
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
card.classList.remove(activeCardClass)
|
card.classList.remove(activeCardClass)
|
||||||
card.querySelector('.modifier-card-image-overlay').innerText = '+'
|
card.querySelector(".modifier-card-image-overlay").innerText = "+"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function changePreviewImages(val) {
|
function changePreviewImages(val) {
|
||||||
const previewImages = document.querySelectorAll('.modifier-card-image-container img')
|
const previewImages = document.querySelectorAll(".modifier-card-image-container img")
|
||||||
|
|
||||||
let previewArr = []
|
let previewArr = []
|
||||||
|
|
||||||
modifiers.map(x => x.modifiers).forEach(x => previewArr.push(...x.map(m => m.previews)))
|
modifiers.map((x) => x.modifiers).forEach((x) => previewArr.push(...x.map((m) => m.previews)))
|
||||||
|
|
||||||
previewArr = previewArr.map(x => {
|
previewArr = previewArr.map((x) => {
|
||||||
let obj = {}
|
let obj = {}
|
||||||
|
|
||||||
x.forEach(preview => {
|
x.forEach((preview) => {
|
||||||
obj[preview.name] = preview.path
|
obj[preview.name] = preview.path
|
||||||
})
|
})
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
})
|
})
|
||||||
|
|
||||||
previewImages.forEach(previewImage => {
|
previewImages.forEach((previewImage) => {
|
||||||
const currentPreviewType = previewImage.getAttribute('preview-type')
|
const currentPreviewType = previewImage.getAttribute("preview-type")
|
||||||
const relativePreviewPath = previewImage.src.split(modifierThumbnailPath + '/').pop()
|
const relativePreviewPath = previewImage.src.split(modifierThumbnailPath + "/").pop()
|
||||||
|
|
||||||
const previews = previewArr.find(preview => relativePreviewPath == preview[currentPreviewType])
|
const previews = previewArr.find((preview) => relativePreviewPath == preview[currentPreviewType])
|
||||||
|
|
||||||
if(typeof previews == 'object') {
|
if (typeof previews == "object") {
|
||||||
let preview = null
|
let preview = null
|
||||||
|
|
||||||
if (val == 'portrait') {
|
if (val == "portrait") {
|
||||||
preview = previews.portrait
|
preview = previews.portrait
|
||||||
}
|
} else if (val == "landscape") {
|
||||||
else if (val == 'landscape') {
|
|
||||||
preview = previews.landscape
|
preview = previews.landscape
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preview != null) {
|
if (preview != null) {
|
||||||
previewImage.src = `${modifierThumbnailPath}/${preview}`
|
previewImage.src = `${modifierThumbnailPath}/${preview}`
|
||||||
previewImage.setAttribute('preview-type', val)
|
previewImage.setAttribute("preview-type", val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function resizeModifierCards(val) {
|
function resizeModifierCards(val) {
|
||||||
const cardSizePrefix = 'modifier-card-size_'
|
const cardSizePrefix = "modifier-card-size_"
|
||||||
const modifierCardClass = 'modifier-card'
|
const modifierCardClass = "modifier-card"
|
||||||
|
|
||||||
const modifierCards = document.querySelectorAll(`.${modifierCardClass}`)
|
const modifierCards = document.querySelectorAll(`.${modifierCardClass}`)
|
||||||
const cardSize = n => `${cardSizePrefix}${n}`
|
const cardSize = (n) => `${cardSizePrefix}${n}`
|
||||||
|
|
||||||
modifierCards.forEach(card => {
|
modifierCards.forEach((card) => {
|
||||||
// remove existing size classes
|
// remove existing size classes
|
||||||
const classes = card.className.split(' ').filter(c => !c.startsWith(cardSizePrefix))
|
const classes = card.className.split(" ").filter((c) => !c.startsWith(cardSizePrefix))
|
||||||
card.className = classes.join(' ').trim()
|
card.className = classes.join(" ").trim()
|
||||||
|
|
||||||
if (val != 0) {
|
if (val != 0) {
|
||||||
card.classList.add(cardSize(val))
|
card.classList.add(cardSize(val))
|
||||||
@ -352,7 +372,7 @@ function resizeModifierCards(val) {
|
|||||||
modifierCardSizeSlider.onchange = () => resizeModifierCards(modifierCardSizeSlider.value)
|
modifierCardSizeSlider.onchange = () => resizeModifierCards(modifierCardSizeSlider.value)
|
||||||
previewImageField.onchange = () => changePreviewImages(previewImageField.value)
|
previewImageField.onchange = () => changePreviewImages(previewImageField.value)
|
||||||
|
|
||||||
modifierSettingsBtn.addEventListener('click', function(e) {
|
modifierSettingsBtn.addEventListener("click", function(e) {
|
||||||
modifierSettingsOverlay.classList.add("active")
|
modifierSettingsOverlay.classList.add("active")
|
||||||
customModifiersTextBox.setSelectionRange(0, 0)
|
customModifiersTextBox.setSelectionRange(0, 0)
|
||||||
customModifiersTextBox.focus()
|
customModifiersTextBox.focus()
|
||||||
@ -360,7 +380,7 @@ modifierSettingsBtn.addEventListener('click', function(e) {
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
})
|
})
|
||||||
|
|
||||||
modifierSettingsOverlay.addEventListener('keydown', function(e) {
|
modifierSettingsOverlay.addEventListener("keydown", function(e) {
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case "Escape": // Escape to cancel
|
case "Escape": // Escape to cancel
|
||||||
customModifiersTextBox.value = customModifiersInitialContent // undo the changes
|
customModifiersTextBox.value = customModifiersInitialContent // undo the changes
|
||||||
@ -368,7 +388,8 @@ modifierSettingsOverlay.addEventListener('keydown', function(e) {
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
break
|
break
|
||||||
case "Enter":
|
case "Enter":
|
||||||
if (e.ctrlKey) { // Ctrl+Enter to confirm
|
if (e.ctrlKey) {
|
||||||
|
// Ctrl+Enter to confirm
|
||||||
modifierSettingsOverlay.classList.remove("active")
|
modifierSettingsOverlay.classList.remove("active")
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
break
|
break
|
||||||
@ -383,7 +404,7 @@ function saveCustomModifiers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadCustomModifiers() {
|
function loadCustomModifiers() {
|
||||||
PLUGINS['MODIFIERS_LOAD'].forEach(fn=>fn.loader.call())
|
PLUGINS["MODIFIERS_LOAD"].forEach((fn) => fn.loader.call())
|
||||||
}
|
}
|
||||||
|
|
||||||
customModifiersTextBox.addEventListener('change', saveCustomModifiers)
|
customModifiersTextBox.addEventListener("change", saveCustomModifiers)
|
||||||
|
1014
ui/media/js/main.js
1014
ui/media/js/main.js
File diff suppressed because it is too large
Load Diff
@ -8,8 +8,8 @@ var ParameterType = {
|
|||||||
select: "select",
|
select: "select",
|
||||||
select_multiple: "select_multiple",
|
select_multiple: "select_multiple",
|
||||||
slider: "slider",
|
slider: "slider",
|
||||||
custom: "custom",
|
custom: "custom"
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSDoc style
|
* JSDoc style
|
||||||
@ -24,7 +24,6 @@ var ParameterType = {
|
|||||||
* @property {boolean?} saveInAppConfig
|
* @property {boolean?} saveInAppConfig
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/** @type {Array.<Parameter>} */
|
/** @type {Array.<Parameter>} */
|
||||||
var PARAMETERS = [
|
var PARAMETERS = [
|
||||||
{
|
{
|
||||||
@ -33,7 +32,8 @@ var PARAMETERS = [
|
|||||||
label: "Theme",
|
label: "Theme",
|
||||||
default: "theme-default",
|
default: "theme-default",
|
||||||
note: "customize the look and feel of the ui",
|
note: "customize the look and feel of the ui",
|
||||||
options: [ // Note: options expanded dynamically
|
options: [
|
||||||
|
// Note: options expanded dynamically
|
||||||
{
|
{
|
||||||
value: "theme-default",
|
value: "theme-default",
|
||||||
label: "Default"
|
label: "Default"
|
||||||
@ -47,7 +47,7 @@ var PARAMETERS = [
|
|||||||
label: "Auto-Save Images",
|
label: "Auto-Save Images",
|
||||||
note: "automatically saves images to the specified location",
|
note: "automatically saves images to the specified location",
|
||||||
icon: "fa-download",
|
icon: "fa-download",
|
||||||
default: false,
|
default: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "diskPath",
|
id: "diskPath",
|
||||||
@ -82,13 +82,13 @@ var PARAMETERS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "embed,txt",
|
value: "embed,txt",
|
||||||
label: "embed & txt",
|
label: "embed & txt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "embed,json",
|
value: "embed,json",
|
||||||
label: "embed & json",
|
label: "embed & json"
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "block_nsfw",
|
id: "block_nsfw",
|
||||||
@ -96,7 +96,7 @@ var PARAMETERS = [
|
|||||||
label: "Block NSFW images",
|
label: "Block NSFW images",
|
||||||
note: "blurs out NSFW images",
|
note: "blurs out NSFW images",
|
||||||
icon: "fa-land-mine-on",
|
icon: "fa-land-mine-on",
|
||||||
default: false,
|
default: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "sound_toggle",
|
id: "sound_toggle",
|
||||||
@ -104,7 +104,7 @@ var PARAMETERS = [
|
|||||||
label: "Enable Sound",
|
label: "Enable Sound",
|
||||||
note: "plays a sound on task completion",
|
note: "plays a sound on task completion",
|
||||||
icon: "fa-volume-low",
|
icon: "fa-volume-low",
|
||||||
default: true,
|
default: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "process_order_toggle",
|
id: "process_order_toggle",
|
||||||
@ -112,7 +112,7 @@ var PARAMETERS = [
|
|||||||
label: "Process newest jobs first",
|
label: "Process newest jobs first",
|
||||||
note: "reverse the normal processing order",
|
note: "reverse the normal processing order",
|
||||||
icon: "fa-arrow-down-short-wide",
|
icon: "fa-arrow-down-short-wide",
|
||||||
default: false,
|
default: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "ui_open_browser_on_start",
|
id: "ui_open_browser_on_start",
|
||||||
@ -121,13 +121,14 @@ var PARAMETERS = [
|
|||||||
note: "starts the default browser on startup",
|
note: "starts the default browser on startup",
|
||||||
icon: "fa-window-restore",
|
icon: "fa-window-restore",
|
||||||
default: true,
|
default: true,
|
||||||
saveInAppConfig: true,
|
saveInAppConfig: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "vram_usage_level",
|
id: "vram_usage_level",
|
||||||
type: ParameterType.select,
|
type: ParameterType.select,
|
||||||
label: "GPU Memory Usage",
|
label: "GPU Memory Usage",
|
||||||
note: "Faster performance requires more GPU memory (VRAM)<br/><br/>" +
|
note:
|
||||||
|
"Faster performance requires more GPU memory (VRAM)<br/><br/>" +
|
||||||
"<b>Balanced:</b> nearly as fast as High, much lower VRAM usage<br/>" +
|
"<b>Balanced:</b> nearly as fast as High, much lower VRAM usage<br/>" +
|
||||||
"<b>High:</b> fastest, maximum GPU memory usage</br>" +
|
"<b>High:</b> fastest, maximum GPU memory usage</br>" +
|
||||||
"<b>Low:</b> slowest, recommended for GPUs with 3 to 4 GB memory",
|
"<b>Low:</b> slowest, recommended for GPUs with 3 to 4 GB memory",
|
||||||
@ -137,7 +138,7 @@ var PARAMETERS = [
|
|||||||
{ value: "balanced", label: "Balanced" },
|
{ value: "balanced", label: "Balanced" },
|
||||||
{ value: "high", label: "High" },
|
{ value: "high", label: "High" },
|
||||||
{ value: "low", label: "Low" }
|
{ value: "low", label: "Low" }
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "use_cpu",
|
id: "use_cpu",
|
||||||
@ -145,20 +146,20 @@ var PARAMETERS = [
|
|||||||
label: "Use CPU (not GPU)",
|
label: "Use CPU (not GPU)",
|
||||||
note: "warning: this will be *very* slow",
|
note: "warning: this will be *very* slow",
|
||||||
icon: "fa-microchip",
|
icon: "fa-microchip",
|
||||||
default: false,
|
default: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "auto_pick_gpus",
|
id: "auto_pick_gpus",
|
||||||
type: ParameterType.checkbox,
|
type: ParameterType.checkbox,
|
||||||
label: "Automatically pick the GPUs (experimental)",
|
label: "Automatically pick the GPUs (experimental)",
|
||||||
default: false,
|
default: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "use_gpus",
|
id: "use_gpus",
|
||||||
type: ParameterType.select_multiple,
|
type: ParameterType.select_multiple,
|
||||||
label: "GPUs to use (experimental)",
|
label: "GPUs to use (experimental)",
|
||||||
note: "to process in parallel",
|
note: "to process in parallel",
|
||||||
default: false,
|
default: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "auto_save_settings",
|
id: "auto_save_settings",
|
||||||
@ -166,15 +167,16 @@ var PARAMETERS = [
|
|||||||
label: "Auto-Save Settings",
|
label: "Auto-Save Settings",
|
||||||
note: "restores settings on browser load",
|
note: "restores settings on browser load",
|
||||||
icon: "fa-gear",
|
icon: "fa-gear",
|
||||||
default: true,
|
default: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "confirm_dangerous_actions",
|
id: "confirm_dangerous_actions",
|
||||||
type: ParameterType.checkbox,
|
type: ParameterType.checkbox,
|
||||||
label: "Confirm dangerous actions",
|
label: "Confirm dangerous actions",
|
||||||
note: "Actions that might lead to data loss must either be clicked with the shift key pressed, or confirmed in an 'Are you sure?' dialog",
|
note:
|
||||||
|
"Actions that might lead to data loss must either be clicked with the shift key pressed, or confirmed in an 'Are you sure?' dialog",
|
||||||
icon: "fa-check-double",
|
icon: "fa-check-double",
|
||||||
default: true,
|
default: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "listen_to_network",
|
id: "listen_to_network",
|
||||||
@ -183,7 +185,7 @@ var PARAMETERS = [
|
|||||||
note: "Other devices on your network can access this web page",
|
note: "Other devices on your network can access this web page",
|
||||||
icon: "fa-network-wired",
|
icon: "fa-network-wired",
|
||||||
default: true,
|
default: true,
|
||||||
saveInAppConfig: true,
|
saveInAppConfig: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "listen_port",
|
id: "listen_port",
|
||||||
@ -194,29 +196,31 @@ var PARAMETERS = [
|
|||||||
render: (parameter) => {
|
render: (parameter) => {
|
||||||
return `<input id="${parameter.id}" name="${parameter.id}" size="6" value="9000" onkeypress="preventNonNumericalInput(event)">`
|
return `<input id="${parameter.id}" name="${parameter.id}" size="6" value="9000" onkeypress="preventNonNumericalInput(event)">`
|
||||||
},
|
},
|
||||||
saveInAppConfig: true,
|
saveInAppConfig: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "use_beta_channel",
|
id: "use_beta_channel",
|
||||||
type: ParameterType.checkbox,
|
type: ParameterType.checkbox,
|
||||||
label: "Beta channel",
|
label: "Beta channel",
|
||||||
note: "Get the latest features immediately (but could be less stable). Please restart the program after changing this.",
|
note:
|
||||||
|
"Get the latest features immediately (but could be less stable). Please restart the program after changing this.",
|
||||||
icon: "fa-fire",
|
icon: "fa-fire",
|
||||||
default: false,
|
default: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "test_diffusers",
|
id: "test_diffusers",
|
||||||
type: ParameterType.checkbox,
|
type: ParameterType.checkbox,
|
||||||
label: "Test Diffusers",
|
label: "Test Diffusers",
|
||||||
note: "<b>Experimental! Can have bugs!</b> Use upcoming features (like LoRA) in our new engine. Please press Save, then restart the program after changing this.",
|
note:
|
||||||
|
"<b>Experimental! Can have bugs!</b> Use upcoming features (like LoRA) in our new engine. Please press Save, then restart the program after changing this.",
|
||||||
icon: "fa-bolt",
|
icon: "fa-bolt",
|
||||||
default: false,
|
default: false,
|
||||||
saveInAppConfig: true,
|
saveInAppConfig: true
|
||||||
},
|
}
|
||||||
];
|
]
|
||||||
|
|
||||||
function getParameterSettingsEntry(id) {
|
function getParameterSettingsEntry(id) {
|
||||||
let parameter = PARAMETERS.filter(p => p.id === id)
|
let parameter = PARAMETERS.filter((p) => p.id === id)
|
||||||
if (parameter.length === 0) {
|
if (parameter.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -224,12 +228,12 @@ function getParameterSettingsEntry(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sliderUpdate(event) {
|
function sliderUpdate(event) {
|
||||||
if (event.srcElement.id.endsWith('-input')) {
|
if (event.srcElement.id.endsWith("-input")) {
|
||||||
let slider = document.getElementById(event.srcElement.id.slice(0, -6))
|
let slider = document.getElementById(event.srcElement.id.slice(0, -6))
|
||||||
slider.value = event.srcElement.value
|
slider.value = event.srcElement.value
|
||||||
slider.dispatchEvent(new Event("change"))
|
slider.dispatchEvent(new Event("change"))
|
||||||
} else {
|
} else {
|
||||||
let field = document.getElementById(event.srcElement.id+'-input')
|
let field = document.getElementById(event.srcElement.id + "-input")
|
||||||
field.value = event.srcElement.value
|
field.value = event.srcElement.value
|
||||||
field.dispatchEvent(new Event("change"))
|
field.dispatchEvent(new Event("change"))
|
||||||
}
|
}
|
||||||
@ -242,19 +246,21 @@ function sliderUpdate(event) {
|
|||||||
function getParameterElement(parameter) {
|
function getParameterElement(parameter) {
|
||||||
switch (parameter.type) {
|
switch (parameter.type) {
|
||||||
case ParameterType.checkbox:
|
case ParameterType.checkbox:
|
||||||
var is_checked = parameter.default ? " checked" : "";
|
var is_checked = parameter.default ? " checked" : ""
|
||||||
return `<input id="${parameter.id}" name="${parameter.id}"${is_checked} type="checkbox">`
|
return `<input id="${parameter.id}" name="${parameter.id}"${is_checked} type="checkbox">`
|
||||||
case ParameterType.select:
|
case ParameterType.select:
|
||||||
case ParameterType.select_multiple:
|
case ParameterType.select_multiple:
|
||||||
var options = (parameter.options || []).map(option => `<option value="${option.value}">${option.label}</option>`).join("")
|
var options = (parameter.options || [])
|
||||||
var multiple = (parameter.type == ParameterType.select_multiple ? 'multiple' : '')
|
.map((option) => `<option value="${option.value}">${option.label}</option>`)
|
||||||
|
.join("")
|
||||||
|
var multiple = parameter.type == ParameterType.select_multiple ? "multiple" : ""
|
||||||
return `<select id="${parameter.id}" name="${parameter.id}" ${multiple}>${options}</select>`
|
return `<select id="${parameter.id}" name="${parameter.id}" ${multiple}>${options}</select>`
|
||||||
case ParameterType.slider:
|
case ParameterType.slider:
|
||||||
return `<input id="${parameter.id}" name="${parameter.id}" class="editor-slider" type="range" value="${parameter.default}" min="${parameter.slider_min}" max="${parameter.slider_max}" oninput="sliderUpdate(event)"> <input id="${parameter.id}-input" name="${parameter.id}-input" size="4" value="${parameter.default}" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)" oninput="sliderUpdate(event)"> ${parameter.slider_unit}`
|
return `<input id="${parameter.id}" name="${parameter.id}" class="editor-slider" type="range" value="${parameter.default}" min="${parameter.slider_min}" max="${parameter.slider_max}" oninput="sliderUpdate(event)"> <input id="${parameter.id}-input" name="${parameter.id}-input" size="4" value="${parameter.default}" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)" oninput="sliderUpdate(event)"> ${parameter.slider_unit}`
|
||||||
case ParameterType.custom:
|
case ParameterType.custom:
|
||||||
return parameter.render(parameter)
|
return parameter.render(parameter)
|
||||||
default:
|
default:
|
||||||
console.error(`Invalid type ${parameter.type} for parameter ${parameter.id}`);
|
console.error(`Invalid type ${parameter.type} for parameter ${parameter.id}`)
|
||||||
return "ERROR: Invalid Type"
|
return "ERROR: Invalid Type"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,31 +271,31 @@ let parametersTable = document.querySelector("#system-settings .parameters-table
|
|||||||
* @param {Array<Parameter> | undefined} parameters
|
* @param {Array<Parameter> | undefined} parameters
|
||||||
* */
|
* */
|
||||||
function initParameters(parameters) {
|
function initParameters(parameters) {
|
||||||
parameters.forEach(parameter => {
|
parameters.forEach((parameter) => {
|
||||||
const element = getParameterElement(parameter)
|
const element = getParameterElement(parameter)
|
||||||
const elementWrapper = createElement('div')
|
const elementWrapper = createElement("div")
|
||||||
if (element instanceof Node) {
|
if (element instanceof Node) {
|
||||||
elementWrapper.appendChild(element)
|
elementWrapper.appendChild(element)
|
||||||
} else {
|
} else {
|
||||||
elementWrapper.innerHTML = element
|
elementWrapper.innerHTML = element
|
||||||
}
|
}
|
||||||
|
|
||||||
const note = typeof parameter.note === 'function' ? parameter.note(parameter) : parameter.note
|
const note = typeof parameter.note === "function" ? parameter.note(parameter) : parameter.note
|
||||||
const noteElements = []
|
const noteElements = []
|
||||||
if (note) {
|
if (note) {
|
||||||
const noteElement = createElement('small')
|
const noteElement = createElement("small")
|
||||||
if (note instanceof Node) {
|
if (note instanceof Node) {
|
||||||
noteElement.appendChild(note)
|
noteElement.appendChild(note)
|
||||||
} else {
|
} else {
|
||||||
noteElement.innerHTML = note || ''
|
noteElement.innerHTML = note || ""
|
||||||
}
|
}
|
||||||
noteElements.push(noteElement)
|
noteElements.push(noteElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
const icon = parameter.icon ? [createElement('i', undefined, ['fa', parameter.icon])] : []
|
const icon = parameter.icon ? [createElement("i", undefined, ["fa", parameter.icon])] : []
|
||||||
|
|
||||||
const label = typeof parameter.label === 'function' ? parameter.label(parameter) : parameter.label
|
const label = typeof parameter.label === "function" ? parameter.label(parameter) : parameter.label
|
||||||
const labelElement = createElement('label', { for: parameter.id })
|
const labelElement = createElement("label", { for: parameter.id })
|
||||||
if (label instanceof Node) {
|
if (label instanceof Node) {
|
||||||
labelElement.appendChild(label)
|
labelElement.appendChild(label)
|
||||||
} else {
|
} else {
|
||||||
@ -297,13 +303,13 @@ function initParameters(parameters) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newrow = createElement(
|
const newrow = createElement(
|
||||||
'div',
|
"div",
|
||||||
{ 'data-setting-id': parameter.id, 'data-save-in-app-config': parameter.saveInAppConfig },
|
{ "data-setting-id": parameter.id, "data-save-in-app-config": parameter.saveInAppConfig },
|
||||||
undefined,
|
undefined,
|
||||||
[
|
[
|
||||||
createElement('div', undefined, undefined, icon),
|
createElement("div", undefined, undefined, icon),
|
||||||
createElement('div', undefined, undefined, [labelElement, ...noteElements]),
|
createElement("div", undefined, undefined, [labelElement, ...noteElements]),
|
||||||
elementWrapper,
|
elementWrapper
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
parametersTable.appendChild(newrow)
|
parametersTable.appendChild(newrow)
|
||||||
@ -314,22 +320,25 @@ function initParameters(parameters) {
|
|||||||
initParameters(PARAMETERS)
|
initParameters(PARAMETERS)
|
||||||
|
|
||||||
// listen to parameters from plugins
|
// listen to parameters from plugins
|
||||||
PARAMETERS.addEventListener('push', (...items) => {
|
PARAMETERS.addEventListener("push", (...items) => {
|
||||||
initParameters(items)
|
initParameters(items)
|
||||||
|
|
||||||
if (items.find(item => item.saveInAppConfig)) {
|
if (items.find((item) => item.saveInAppConfig)) {
|
||||||
console.log('Reloading app config for new parameters', items.map(p => p.id))
|
console.log(
|
||||||
|
"Reloading app config for new parameters",
|
||||||
|
items.map((p) => p.id)
|
||||||
|
)
|
||||||
getAppConfig()
|
getAppConfig()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let vramUsageLevelField = document.querySelector('#vram_usage_level')
|
let vramUsageLevelField = document.querySelector("#vram_usage_level")
|
||||||
let useCPUField = document.querySelector('#use_cpu')
|
let useCPUField = document.querySelector("#use_cpu")
|
||||||
let autoPickGPUsField = document.querySelector('#auto_pick_gpus')
|
let autoPickGPUsField = document.querySelector("#auto_pick_gpus")
|
||||||
let useGPUsField = document.querySelector('#use_gpus')
|
let useGPUsField = document.querySelector("#use_gpus")
|
||||||
let saveToDiskField = document.querySelector('#save_to_disk')
|
let saveToDiskField = document.querySelector("#save_to_disk")
|
||||||
let diskPathField = document.querySelector('#diskPath')
|
let diskPathField = document.querySelector("#diskPath")
|
||||||
let metadataOutputFormatField = document.querySelector('#metadata_output_format')
|
let metadataOutputFormatField = document.querySelector("#metadata_output_format")
|
||||||
let listenToNetworkField = document.querySelector("#listen_to_network")
|
let listenToNetworkField = document.querySelector("#listen_to_network")
|
||||||
let listenPortField = document.querySelector("#listen_port")
|
let listenPortField = document.querySelector("#listen_port")
|
||||||
let useBetaChannelField = document.querySelector("#use_beta_channel")
|
let useBetaChannelField = document.querySelector("#use_beta_channel")
|
||||||
@ -337,35 +346,34 @@ let uiOpenBrowserOnStartField = document.querySelector("#ui_open_browser_on_star
|
|||||||
let confirmDangerousActionsField = document.querySelector("#confirm_dangerous_actions")
|
let confirmDangerousActionsField = document.querySelector("#confirm_dangerous_actions")
|
||||||
let testDiffusers = document.querySelector("#test_diffusers")
|
let testDiffusers = document.querySelector("#test_diffusers")
|
||||||
|
|
||||||
let saveSettingsBtn = document.querySelector('#save-system-settings-btn')
|
let saveSettingsBtn = document.querySelector("#save-system-settings-btn")
|
||||||
|
|
||||||
|
|
||||||
async function changeAppConfig(configDelta) {
|
async function changeAppConfig(configDelta) {
|
||||||
try {
|
try {
|
||||||
let res = await fetch('/app_config', {
|
let res = await fetch("/app_config", {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify(configDelta)
|
body: JSON.stringify(configDelta)
|
||||||
})
|
})
|
||||||
res = await res.json()
|
res = await res.json()
|
||||||
|
|
||||||
console.log('set config status response', res)
|
console.log("set config status response", res)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('set config status error', e)
|
console.log("set config status error", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAppConfig() {
|
async function getAppConfig() {
|
||||||
try {
|
try {
|
||||||
let res = await fetch('/get/app_config')
|
let res = await fetch("/get/app_config")
|
||||||
const config = await res.json()
|
const config = await res.json()
|
||||||
|
|
||||||
applySettingsFromConfig(config)
|
applySettingsFromConfig(config)
|
||||||
|
|
||||||
// custom overrides
|
// custom overrides
|
||||||
if (config.update_branch === 'beta') {
|
if (config.update_branch === "beta") {
|
||||||
useBetaChannelField.checked = true
|
useBetaChannelField.checked = true
|
||||||
document.querySelector("#updateBranchLabel").innerText = "(beta)"
|
document.querySelector("#updateBranchLabel").innerText = "(beta)"
|
||||||
} else {
|
} else {
|
||||||
@ -380,45 +388,48 @@ async function getAppConfig() {
|
|||||||
if (config.net && config.net.listen_port !== undefined) {
|
if (config.net && config.net.listen_port !== undefined) {
|
||||||
listenPortField.value = config.net.listen_port
|
listenPortField.value = config.net.listen_port
|
||||||
}
|
}
|
||||||
if (config.test_diffusers === undefined || config.update_branch === 'main') {
|
if (config.test_diffusers === undefined || config.update_branch === "main") {
|
||||||
testDiffusers.checked = false
|
testDiffusers.checked = false
|
||||||
document.querySelector("#lora_model_container").style.display = 'none'
|
document.querySelector("#lora_model_container").style.display = "none"
|
||||||
document.querySelector("#lora_alpha_container").style.display = 'none'
|
document.querySelector("#lora_alpha_container").style.display = "none"
|
||||||
} else {
|
} else {
|
||||||
testDiffusers.checked = config.test_diffusers && config.update_branch !== 'main'
|
testDiffusers.checked = config.test_diffusers && config.update_branch !== "main"
|
||||||
document.querySelector("#lora_model_container").style.display = (testDiffusers.checked ? '' : 'none')
|
document.querySelector("#lora_model_container").style.display = testDiffusers.checked ? "" : "none"
|
||||||
document.querySelector("#lora_alpha_container").style.display = (testDiffusers.checked && loraModelField.value !== "" ? '' : 'none')
|
document.querySelector("#lora_alpha_container").style.display =
|
||||||
|
testDiffusers.checked && loraModelField.value !== "" ? "" : "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('get config status response', config)
|
console.log("get config status response", config)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('get config status error', e)
|
console.log("get config status error", e)
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applySettingsFromConfig(config) {
|
function applySettingsFromConfig(config) {
|
||||||
Array.from(parametersTable.children).forEach(parameterRow => {
|
Array.from(parametersTable.children).forEach((parameterRow) => {
|
||||||
if (parameterRow.dataset.settingId in config && parameterRow.dataset.saveInAppConfig === 'true') {
|
if (parameterRow.dataset.settingId in config && parameterRow.dataset.saveInAppConfig === "true") {
|
||||||
const configValue = config[parameterRow.dataset.settingId]
|
const configValue = config[parameterRow.dataset.settingId]
|
||||||
const parameterElement = document.getElementById(parameterRow.dataset.settingId) ||
|
const parameterElement =
|
||||||
parameterRow.querySelector('input') || parameterRow.querySelector('select')
|
document.getElementById(parameterRow.dataset.settingId) ||
|
||||||
|
parameterRow.querySelector("input") ||
|
||||||
|
parameterRow.querySelector("select")
|
||||||
|
|
||||||
switch (parameterElement?.tagName) {
|
switch (parameterElement?.tagName) {
|
||||||
case 'INPUT':
|
case "INPUT":
|
||||||
if (parameterElement.type === 'checkbox') {
|
if (parameterElement.type === "checkbox") {
|
||||||
parameterElement.checked = configValue
|
parameterElement.checked = configValue
|
||||||
} else {
|
} else {
|
||||||
parameterElement.value = configValue
|
parameterElement.value = configValue
|
||||||
}
|
}
|
||||||
parameterElement.dispatchEvent(new Event('change'))
|
parameterElement.dispatchEvent(new Event("change"))
|
||||||
break
|
break
|
||||||
case 'SELECT':
|
case "SELECT":
|
||||||
if (Array.isArray(configValue)) {
|
if (Array.isArray(configValue)) {
|
||||||
Array.from(parameterElement.options).forEach(option => {
|
Array.from(parameterElement.options).forEach((option) => {
|
||||||
if (configValue.includes(option.value || option.text)) {
|
if (configValue.includes(option.value || option.text)) {
|
||||||
option.selected = true
|
option.selected = true
|
||||||
}
|
}
|
||||||
@ -426,82 +437,85 @@ function applySettingsFromConfig(config) {
|
|||||||
} else {
|
} else {
|
||||||
parameterElement.value = configValue
|
parameterElement.value = configValue
|
||||||
}
|
}
|
||||||
parameterElement.dispatchEvent(new Event('change'))
|
parameterElement.dispatchEvent(new Event("change"))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
saveToDiskField.addEventListener('change', function(e) {
|
saveToDiskField.addEventListener("change", function(e) {
|
||||||
diskPathField.disabled = !this.checked
|
diskPathField.disabled = !this.checked
|
||||||
metadataOutputFormatField.disabled = !this.checked
|
metadataOutputFormatField.disabled = !this.checked
|
||||||
})
|
})
|
||||||
|
|
||||||
function getCurrentRenderDeviceSelection() {
|
function getCurrentRenderDeviceSelection() {
|
||||||
let selectedGPUs = $('#use_gpus').val()
|
let selectedGPUs = $("#use_gpus").val()
|
||||||
|
|
||||||
if (useCPUField.checked && !autoPickGPUsField.checked) {
|
if (useCPUField.checked && !autoPickGPUsField.checked) {
|
||||||
return 'cpu'
|
return "cpu"
|
||||||
}
|
}
|
||||||
if (autoPickGPUsField.checked || selectedGPUs.length == 0) {
|
if (autoPickGPUsField.checked || selectedGPUs.length == 0) {
|
||||||
return 'auto'
|
return "auto"
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectedGPUs.join(',')
|
return selectedGPUs.join(",")
|
||||||
}
|
}
|
||||||
|
|
||||||
useCPUField.addEventListener('click', function() {
|
useCPUField.addEventListener("click", function() {
|
||||||
let gpuSettingEntry = getParameterSettingsEntry('use_gpus')
|
let gpuSettingEntry = getParameterSettingsEntry("use_gpus")
|
||||||
let autoPickGPUSettingEntry = getParameterSettingsEntry('auto_pick_gpus')
|
let autoPickGPUSettingEntry = getParameterSettingsEntry("auto_pick_gpus")
|
||||||
if (this.checked) {
|
if (this.checked) {
|
||||||
gpuSettingEntry.style.display = 'none'
|
gpuSettingEntry.style.display = "none"
|
||||||
autoPickGPUSettingEntry.style.display = 'none'
|
autoPickGPUSettingEntry.style.display = "none"
|
||||||
autoPickGPUsField.setAttribute('data-old-value', autoPickGPUsField.checked)
|
autoPickGPUsField.setAttribute("data-old-value", autoPickGPUsField.checked)
|
||||||
autoPickGPUsField.checked = false
|
autoPickGPUsField.checked = false
|
||||||
} else if (useGPUsField.options.length >= MIN_GPUS_TO_SHOW_SELECTION) {
|
} else if (useGPUsField.options.length >= MIN_GPUS_TO_SHOW_SELECTION) {
|
||||||
gpuSettingEntry.style.display = ''
|
gpuSettingEntry.style.display = ""
|
||||||
autoPickGPUSettingEntry.style.display = ''
|
autoPickGPUSettingEntry.style.display = ""
|
||||||
let oldVal = autoPickGPUsField.getAttribute('data-old-value')
|
let oldVal = autoPickGPUsField.getAttribute("data-old-value")
|
||||||
if (oldVal === null || oldVal === undefined) { // the UI started with CPU selected by default
|
if (oldVal === null || oldVal === undefined) {
|
||||||
|
// the UI started with CPU selected by default
|
||||||
autoPickGPUsField.checked = true
|
autoPickGPUsField.checked = true
|
||||||
} else {
|
} else {
|
||||||
autoPickGPUsField.checked = (oldVal === 'true')
|
autoPickGPUsField.checked = oldVal === "true"
|
||||||
}
|
}
|
||||||
gpuSettingEntry.style.display = (autoPickGPUsField.checked ? 'none' : '')
|
gpuSettingEntry.style.display = autoPickGPUsField.checked ? "none" : ""
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
useGPUsField.addEventListener('click', function() {
|
useGPUsField.addEventListener("click", function() {
|
||||||
let selectedGPUs = $('#use_gpus').val()
|
let selectedGPUs = $("#use_gpus").val()
|
||||||
autoPickGPUsField.checked = (selectedGPUs.length === 0)
|
autoPickGPUsField.checked = selectedGPUs.length === 0
|
||||||
})
|
})
|
||||||
|
|
||||||
autoPickGPUsField.addEventListener('click', function() {
|
autoPickGPUsField.addEventListener("click", function() {
|
||||||
if (this.checked) {
|
if (this.checked) {
|
||||||
$('#use_gpus').val([])
|
$("#use_gpus").val([])
|
||||||
}
|
}
|
||||||
|
|
||||||
let gpuSettingEntry = getParameterSettingsEntry('use_gpus')
|
let gpuSettingEntry = getParameterSettingsEntry("use_gpus")
|
||||||
gpuSettingEntry.style.display = (this.checked ? 'none' : '')
|
gpuSettingEntry.style.display = this.checked ? "none" : ""
|
||||||
})
|
})
|
||||||
|
|
||||||
async function setDiskPath(defaultDiskPath, force = false) {
|
async function setDiskPath(defaultDiskPath, force = false) {
|
||||||
var diskPath = getSetting("diskPath")
|
var diskPath = getSetting("diskPath")
|
||||||
if (force || diskPath == '' || diskPath == undefined || diskPath == "undefined") {
|
if (force || diskPath == "" || diskPath == undefined || diskPath == "undefined") {
|
||||||
setSetting("diskPath", defaultDiskPath)
|
setSetting("diskPath", defaultDiskPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDeviceInfo(devices) {
|
function setDeviceInfo(devices) {
|
||||||
let cpu = devices.all.cpu.name
|
let cpu = devices.all.cpu.name
|
||||||
let allGPUs = Object.keys(devices.all).filter(d => d != 'cpu')
|
let allGPUs = Object.keys(devices.all).filter((d) => d != "cpu")
|
||||||
let activeGPUs = Object.keys(devices.active)
|
let activeGPUs = Object.keys(devices.active)
|
||||||
|
|
||||||
function ID_TO_TEXT(d) {
|
function ID_TO_TEXT(d) {
|
||||||
let info = devices.all[d]
|
let info = devices.all[d]
|
||||||
if ("mem_free" in info && "mem_total" in info) {
|
if ("mem_free" in info && "mem_total" in info) {
|
||||||
return `${info.name} <small>(${d}) (${info.mem_free.toFixed(1)}Gb free / ${info.mem_total.toFixed(1)} Gb total)</small>`
|
return `${info.name} <small>(${d}) (${info.mem_free.toFixed(1)}Gb free / ${info.mem_total.toFixed(
|
||||||
|
1
|
||||||
|
)} Gb total)</small>`
|
||||||
} else {
|
} else {
|
||||||
return `${info.name} <small>(${d}) (no memory info)</small>`
|
return `${info.name} <small>(${d}) (no memory info)</small>`
|
||||||
}
|
}
|
||||||
@ -510,35 +524,35 @@ function setDeviceInfo(devices) {
|
|||||||
allGPUs = allGPUs.map(ID_TO_TEXT)
|
allGPUs = allGPUs.map(ID_TO_TEXT)
|
||||||
activeGPUs = activeGPUs.map(ID_TO_TEXT)
|
activeGPUs = activeGPUs.map(ID_TO_TEXT)
|
||||||
|
|
||||||
let systemInfoEl = document.querySelector('#system-info')
|
let systemInfoEl = document.querySelector("#system-info")
|
||||||
systemInfoEl.querySelector('#system-info-cpu').innerText = cpu
|
systemInfoEl.querySelector("#system-info-cpu").innerText = cpu
|
||||||
systemInfoEl.querySelector('#system-info-gpus-all').innerHTML = allGPUs.join('</br>')
|
systemInfoEl.querySelector("#system-info-gpus-all").innerHTML = allGPUs.join("</br>")
|
||||||
systemInfoEl.querySelector('#system-info-rendering-devices').innerHTML = activeGPUs.join('</br>')
|
systemInfoEl.querySelector("#system-info-rendering-devices").innerHTML = activeGPUs.join("</br>")
|
||||||
}
|
}
|
||||||
|
|
||||||
function setHostInfo(hosts) {
|
function setHostInfo(hosts) {
|
||||||
let port = listenPortField.value
|
let port = listenPortField.value
|
||||||
hosts = hosts.map(addr => `http://${addr}:${port}/`).map(url => `<div><a href="${url}">${url}</a></div>`)
|
hosts = hosts.map((addr) => `http://${addr}:${port}/`).map((url) => `<div><a href="${url}">${url}</a></div>`)
|
||||||
document.querySelector('#system-info-server-hosts').innerHTML = hosts.join('')
|
document.querySelector("#system-info-server-hosts").innerHTML = hosts.join("")
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSystemInfo() {
|
async function getSystemInfo() {
|
||||||
try {
|
try {
|
||||||
const res = await SD.getSystemInfo()
|
const res = await SD.getSystemInfo()
|
||||||
let devices = res['devices']
|
let devices = res["devices"]
|
||||||
|
|
||||||
let allDeviceIds = Object.keys(devices['all']).filter(d => d !== 'cpu')
|
let allDeviceIds = Object.keys(devices["all"]).filter((d) => d !== "cpu")
|
||||||
let activeDeviceIds = Object.keys(devices['active']).filter(d => d !== 'cpu')
|
let activeDeviceIds = Object.keys(devices["active"]).filter((d) => d !== "cpu")
|
||||||
|
|
||||||
if (activeDeviceIds.length === 0) {
|
if (activeDeviceIds.length === 0) {
|
||||||
useCPUField.checked = true
|
useCPUField.checked = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allDeviceIds.length < MIN_GPUS_TO_SHOW_SELECTION || useCPUField.checked) {
|
if (allDeviceIds.length < MIN_GPUS_TO_SHOW_SELECTION || useCPUField.checked) {
|
||||||
let gpuSettingEntry = getParameterSettingsEntry('use_gpus')
|
let gpuSettingEntry = getParameterSettingsEntry("use_gpus")
|
||||||
gpuSettingEntry.style.display = 'none'
|
gpuSettingEntry.style.display = "none"
|
||||||
let autoPickGPUSettingEntry = getParameterSettingsEntry('auto_pick_gpus')
|
let autoPickGPUSettingEntry = getParameterSettingsEntry("auto_pick_gpus")
|
||||||
autoPickGPUSettingEntry.style.display = 'none'
|
autoPickGPUSettingEntry.style.display = "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allDeviceIds.length === 0) {
|
if (allDeviceIds.length === 0) {
|
||||||
@ -546,27 +560,27 @@ async function getSystemInfo() {
|
|||||||
useCPUField.disabled = true // no compatible GPUs, so make the CPU mandatory
|
useCPUField.disabled = true // no compatible GPUs, so make the CPU mandatory
|
||||||
}
|
}
|
||||||
|
|
||||||
autoPickGPUsField.checked = (devices['config'] === 'auto')
|
autoPickGPUsField.checked = devices["config"] === "auto"
|
||||||
|
|
||||||
useGPUsField.innerHTML = ''
|
useGPUsField.innerHTML = ""
|
||||||
allDeviceIds.forEach(device => {
|
allDeviceIds.forEach((device) => {
|
||||||
let deviceName = devices['all'][device]['name']
|
let deviceName = devices["all"][device]["name"]
|
||||||
let deviceOption = `<option value="${device}">${deviceName} (${device})</option>`
|
let deviceOption = `<option value="${device}">${deviceName} (${device})</option>`
|
||||||
useGPUsField.insertAdjacentHTML('beforeend', deviceOption)
|
useGPUsField.insertAdjacentHTML("beforeend", deviceOption)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (autoPickGPUsField.checked) {
|
if (autoPickGPUsField.checked) {
|
||||||
let gpuSettingEntry = getParameterSettingsEntry('use_gpus')
|
let gpuSettingEntry = getParameterSettingsEntry("use_gpus")
|
||||||
gpuSettingEntry.style.display = 'none'
|
gpuSettingEntry.style.display = "none"
|
||||||
} else {
|
} else {
|
||||||
$('#use_gpus').val(activeDeviceIds)
|
$("#use_gpus").val(activeDeviceIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
setDeviceInfo(devices)
|
setDeviceInfo(devices)
|
||||||
setHostInfo(res['hosts'])
|
setHostInfo(res["hosts"])
|
||||||
let force = false
|
let force = false
|
||||||
if (res['enforce_output_dir'] !== undefined) {
|
if (res["enforce_output_dir"] !== undefined) {
|
||||||
force = res['enforce_output_dir']
|
force = res["enforce_output_dir"]
|
||||||
if (force == true) {
|
if (force == true) {
|
||||||
saveToDiskField.checked = true
|
saveToDiskField.checked = true
|
||||||
metadataOutputFormatField.disabled = false
|
metadataOutputFormatField.disabled = false
|
||||||
@ -574,58 +588,62 @@ async function getSystemInfo() {
|
|||||||
saveToDiskField.disabled = force
|
saveToDiskField.disabled = force
|
||||||
diskPathField.disabled = force
|
diskPathField.disabled = force
|
||||||
}
|
}
|
||||||
setDiskPath(res['default_output_dir'], force)
|
setDiskPath(res["default_output_dir"], force)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('error fetching devices', e)
|
console.log("error fetching devices", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveSettingsBtn.addEventListener('click', function() {
|
saveSettingsBtn.addEventListener("click", function() {
|
||||||
if (listenPortField.value == '') {
|
if (listenPortField.value == "") {
|
||||||
alert('The network port field must not be empty.')
|
alert("The network port field must not be empty.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (listenPortField.value < 1 || listenPortField.value > 65535) {
|
if (listenPortField.value < 1 || listenPortField.value > 65535) {
|
||||||
alert('The network port must be a number from 1 to 65535')
|
alert("The network port must be a number from 1 to 65535")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const updateBranch = (useBetaChannelField.checked ? 'beta' : 'main')
|
const updateBranch = useBetaChannelField.checked ? "beta" : "main"
|
||||||
|
|
||||||
const updateAppConfigRequest = {
|
const updateAppConfigRequest = {
|
||||||
'render_devices': getCurrentRenderDeviceSelection(),
|
render_devices: getCurrentRenderDeviceSelection(),
|
||||||
'update_branch': updateBranch,
|
update_branch: updateBranch
|
||||||
}
|
}
|
||||||
|
|
||||||
Array.from(parametersTable.children).forEach(parameterRow => {
|
Array.from(parametersTable.children).forEach((parameterRow) => {
|
||||||
if (parameterRow.dataset.saveInAppConfig === 'true') {
|
if (parameterRow.dataset.saveInAppConfig === "true") {
|
||||||
const parameterElement = document.getElementById(parameterRow.dataset.settingId) ||
|
const parameterElement =
|
||||||
parameterRow.querySelector('input') || parameterRow.querySelector('select')
|
document.getElementById(parameterRow.dataset.settingId) ||
|
||||||
|
parameterRow.querySelector("input") ||
|
||||||
|
parameterRow.querySelector("select")
|
||||||
|
|
||||||
switch (parameterElement?.tagName) {
|
switch (parameterElement?.tagName) {
|
||||||
case 'INPUT':
|
case "INPUT":
|
||||||
if (parameterElement.type === 'checkbox') {
|
if (parameterElement.type === "checkbox") {
|
||||||
updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.checked
|
updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.checked
|
||||||
} else {
|
} else {
|
||||||
updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.value
|
updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.value
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'SELECT':
|
case "SELECT":
|
||||||
if (parameterElement.multiple) {
|
if (parameterElement.multiple) {
|
||||||
updateAppConfigRequest[parameterRow.dataset.settingId] = Array.from(parameterElement.options)
|
updateAppConfigRequest[parameterRow.dataset.settingId] = Array.from(parameterElement.options)
|
||||||
.filter(option => option.selected)
|
.filter((option) => option.selected)
|
||||||
.map(option => option.value || option.text)
|
.map((option) => option.value || option.text)
|
||||||
} else {
|
} else {
|
||||||
updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.value
|
updateAppConfigRequest[parameterRow.dataset.settingId] = parameterElement.value
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.error(`Setting parameter ${parameterRow.dataset.settingId} couldn't be saved to app.config - element #${parameter.id} is a <${parameterElement?.tagName} /> instead of a <input /> or a <select />!`)
|
console.error(
|
||||||
|
`Setting parameter ${parameterRow.dataset.settingId} couldn't be saved to app.config - element #${parameter.id} is a <${parameterElement?.tagName} /> instead of a <input /> or a <select />!`
|
||||||
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const savePromise = changeAppConfig(updateAppConfigRequest)
|
const savePromise = changeAppConfig(updateAppConfigRequest)
|
||||||
saveSettingsBtn.classList.add('active')
|
saveSettingsBtn.classList.add("active")
|
||||||
Promise.all([savePromise, asyncDelay(300)]).then(() => saveSettingsBtn.classList.remove('active'))
|
Promise.all([savePromise, asyncDelay(300)]).then(() => saveSettingsBtn.classList.remove("active"))
|
||||||
})
|
})
|
||||||
|
@ -29,14 +29,20 @@ const PLUGINS = {
|
|||||||
MODIFIERS_LOAD: [],
|
MODIFIERS_LOAD: [],
|
||||||
TASK_CREATE: [],
|
TASK_CREATE: [],
|
||||||
OUTPUTS_FORMATS: new ServiceContainer(
|
OUTPUTS_FORMATS: new ServiceContainer(
|
||||||
function png() { return (reqBody) => new SD.RenderTask(reqBody) }
|
function png() {
|
||||||
, function jpeg() { return (reqBody) => new SD.RenderTask(reqBody) }
|
return (reqBody) => new SD.RenderTask(reqBody)
|
||||||
, function webp() { return (reqBody) => new SD.RenderTask(reqBody) }
|
},
|
||||||
),
|
function jpeg() {
|
||||||
|
return (reqBody) => new SD.RenderTask(reqBody)
|
||||||
|
},
|
||||||
|
function webp() {
|
||||||
|
return (reqBody) => new SD.RenderTask(reqBody)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
PLUGINS.OUTPUTS_FORMATS.register = function(...args) {
|
PLUGINS.OUTPUTS_FORMATS.register = function(...args) {
|
||||||
const service = ServiceContainer.prototype.register.apply(this, args)
|
const service = ServiceContainer.prototype.register.apply(this, args)
|
||||||
if (typeof outputFormatField !== 'undefined') {
|
if (typeof outputFormatField !== "undefined") {
|
||||||
const newOption = document.createElement("option")
|
const newOption = document.createElement("option")
|
||||||
newOption.setAttribute("value", service.name)
|
newOption.setAttribute("value", service.name)
|
||||||
newOption.innerText = service.name
|
newOption.innerText = service.name
|
||||||
@ -46,13 +52,13 @@ PLUGINS.OUTPUTS_FORMATS.register = function(...args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadScript(url) {
|
function loadScript(url) {
|
||||||
const script = document.createElement('script')
|
const script = document.createElement("script")
|
||||||
const promiseSrc = new PromiseSource()
|
const promiseSrc = new PromiseSource()
|
||||||
script.addEventListener('error', () => promiseSrc.reject(new Error(`Script "${url}" couldn't be loaded.`)))
|
script.addEventListener("error", () => promiseSrc.reject(new Error(`Script "${url}" couldn't be loaded.`)))
|
||||||
script.addEventListener('load', () => promiseSrc.resolve(url))
|
script.addEventListener("load", () => promiseSrc.resolve(url))
|
||||||
script.src = url + '?t=' + Date.now()
|
script.src = url + "?t=" + Date.now()
|
||||||
|
|
||||||
console.log('loading script', url)
|
console.log("loading script", url)
|
||||||
document.head.appendChild(script)
|
document.head.appendChild(script)
|
||||||
|
|
||||||
return promiseSrc.promise
|
return promiseSrc.promise
|
||||||
@ -60,7 +66,7 @@ function loadScript(url) {
|
|||||||
|
|
||||||
async function loadUIPlugins() {
|
async function loadUIPlugins() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/get/ui_plugins')
|
const res = await fetch("/get/ui_plugins")
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error(`Error HTTP${res.status} while loading plugins list. - ${res.statusText}`)
|
console.error(`Error HTTP${res.status} while loading plugins list. - ${res.statusText}`)
|
||||||
return
|
return
|
||||||
@ -69,6 +75,6 @@ async function loadUIPlugins() {
|
|||||||
const loadingPromises = plugins.map(loadScript)
|
const loadingPromises = plugins.map(loadScript)
|
||||||
return await Promise.allSettled(loadingPromises)
|
return await Promise.allSettled(loadingPromises)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('error fetching plugin paths', e)
|
console.log("error fetching plugin paths", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,7 @@ let hypernetworkModelField = new ModelDropdown(document.querySelector('#hypernet
|
|||||||
|
|
||||||
3) Model dropdowns will be refreshed automatically when the reload models button is invoked.
|
3) Model dropdowns will be refreshed automatically when the reload models button is invoked.
|
||||||
*/
|
*/
|
||||||
class ModelDropdown
|
class ModelDropdown {
|
||||||
{
|
|
||||||
modelFilter //= document.querySelector("#model-filter")
|
modelFilter //= document.querySelector("#model-filter")
|
||||||
modelFilterArrow //= document.querySelector("#model-filter-arrow")
|
modelFilterArrow //= document.querySelector("#model-filter-arrow")
|
||||||
modelList //= document.querySelector("#model-list")
|
modelList //= document.querySelector("#model-list")
|
||||||
@ -59,11 +58,11 @@ class ModelDropdown
|
|||||||
set disabled(state) {
|
set disabled(state) {
|
||||||
this.modelFilter.disabled = state
|
this.modelFilter.disabled = state
|
||||||
if (this.modelFilterArrow) {
|
if (this.modelFilterArrow) {
|
||||||
this.modelFilterArrow.style.color = state ? 'dimgray' : ''
|
this.modelFilterArrow.style.color = state ? "dimgray" : ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get modelElements() {
|
get modelElements() {
|
||||||
return this.modelList.querySelectorAll('.model-file')
|
return this.modelList.querySelectorAll(".model-file")
|
||||||
}
|
}
|
||||||
addEventListener(type, listener, options) {
|
addEventListener(type, listener, options) {
|
||||||
return this.modelFilter.addEventListener(type, listener, options)
|
return this.modelFilter.addEventListener(type, listener, options)
|
||||||
@ -83,20 +82,25 @@ class ModelDropdown
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* SEARCHABLE INPUT */
|
/* SEARCHABLE INPUT */
|
||||||
constructor (input, modelKey, noneEntry = '') {
|
|
||||||
|
constructor(input, modelKey, noneEntry = "") {
|
||||||
this.modelFilter = input
|
this.modelFilter = input
|
||||||
this.noneEntry = noneEntry
|
this.noneEntry = noneEntry
|
||||||
this.modelKey = modelKey
|
this.modelKey = modelKey
|
||||||
|
|
||||||
if (modelsOptions !== undefined) { // reuse models from cache (only useful for plugins, which are loaded after models)
|
if (modelsOptions !== undefined) {
|
||||||
|
// reuse models from cache (only useful for plugins, which are loaded after models)
|
||||||
this.inputModels = modelsOptions[this.modelKey]
|
this.inputModels = modelsOptions[this.modelKey]
|
||||||
this.populateModels()
|
this.populateModels()
|
||||||
}
|
}
|
||||||
document.addEventListener("refreshModels", this.bind(function(e) {
|
document.addEventListener(
|
||||||
|
"refreshModels",
|
||||||
|
this.bind(function(e) {
|
||||||
// reload the models
|
// reload the models
|
||||||
this.inputModels = modelsOptions[this.modelKey]
|
this.inputModels = modelsOptions[this.modelKey]
|
||||||
this.populateModels()
|
this.populateModels()
|
||||||
}, this))
|
}, this)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
saveCurrentSelection(elem, value, path) {
|
saveCurrentSelection(elem, value, path) {
|
||||||
@ -105,13 +109,13 @@ class ModelDropdown
|
|||||||
this.currentSelection.path = path
|
this.currentSelection.path = path
|
||||||
this.modelFilter.dataset.path = path
|
this.modelFilter.dataset.path = path
|
||||||
this.modelFilter.value = value
|
this.modelFilter.value = value
|
||||||
this.modelFilter.dispatchEvent(new Event('change'))
|
this.modelFilter.dispatchEvent(new Event("change"))
|
||||||
}
|
}
|
||||||
|
|
||||||
processClick(e) {
|
processClick(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (e.srcElement.classList.contains('model-file') || e.srcElement.classList.contains('fa-file')) {
|
if (e.srcElement.classList.contains("model-file") || e.srcElement.classList.contains("fa-file")) {
|
||||||
const elem = e.srcElement.classList.contains('model-file') ? e.srcElement : e.srcElement.parentElement
|
const elem = e.srcElement.classList.contains("model-file") ? e.srcElement : e.srcElement.parentElement
|
||||||
this.saveCurrentSelection(elem, elem.innerText, elem.dataset.path)
|
this.saveCurrentSelection(elem, elem.innerText, elem.dataset.path)
|
||||||
this.hideModelList()
|
this.hideModelList()
|
||||||
this.modelFilter.focus()
|
this.modelFilter.focus()
|
||||||
@ -126,35 +130,38 @@ class ModelDropdown
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return modelElements.slice(0, index).reverse().find(e => e.style.display === 'list-item')
|
return modelElements
|
||||||
|
.slice(0, index)
|
||||||
|
.reverse()
|
||||||
|
.find((e) => e.style.display === "list-item")
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastVisibleChild(elem) {
|
getLastVisibleChild(elem) {
|
||||||
let lastElementChild = elem.lastElementChild
|
let lastElementChild = elem.lastElementChild
|
||||||
if (lastElementChild.style.display == 'list-item') return lastElementChild
|
if (lastElementChild.style.display == "list-item") return lastElementChild
|
||||||
return this.getPreviousVisibleSibling(lastElementChild)
|
return this.getPreviousVisibleSibling(lastElementChild)
|
||||||
}
|
}
|
||||||
|
|
||||||
getNextVisibleSibling(elem) {
|
getNextVisibleSibling(elem) {
|
||||||
const modelElements = Array.from(this.modelElements)
|
const modelElements = Array.from(this.modelElements)
|
||||||
const index = modelElements.indexOf(elem)
|
const index = modelElements.indexOf(elem)
|
||||||
return modelElements.slice(index + 1).find(e => e.style.display === 'list-item')
|
return modelElements.slice(index + 1).find((e) => e.style.display === "list-item")
|
||||||
}
|
}
|
||||||
|
|
||||||
getFirstVisibleChild(elem) {
|
getFirstVisibleChild(elem) {
|
||||||
let firstElementChild = elem.firstElementChild
|
let firstElementChild = elem.firstElementChild
|
||||||
if (firstElementChild.style.display == 'list-item') return firstElementChild
|
if (firstElementChild.style.display == "list-item") return firstElementChild
|
||||||
return this.getNextVisibleSibling(firstElementChild)
|
return this.getNextVisibleSibling(firstElementChild)
|
||||||
}
|
}
|
||||||
|
|
||||||
selectModelEntry(elem) {
|
selectModelEntry(elem) {
|
||||||
if (elem) {
|
if (elem) {
|
||||||
if (this.highlightedModelEntry !== undefined) {
|
if (this.highlightedModelEntry !== undefined) {
|
||||||
this.highlightedModelEntry.classList.remove('selected')
|
this.highlightedModelEntry.classList.remove("selected")
|
||||||
}
|
}
|
||||||
this.saveCurrentSelection(elem, elem.innerText, elem.dataset.path)
|
this.saveCurrentSelection(elem, elem.innerText, elem.dataset.path)
|
||||||
elem.classList.add('selected')
|
elem.classList.add("selected")
|
||||||
elem.scrollIntoView({block: 'nearest'})
|
elem.scrollIntoView({ block: "nearest" })
|
||||||
this.highlightedModelEntry = elem
|
this.highlightedModelEntry = elem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,11 +170,9 @@ class ModelDropdown
|
|||||||
const elem = this.getPreviousVisibleSibling(this.highlightedModelEntry)
|
const elem = this.getPreviousVisibleSibling(this.highlightedModelEntry)
|
||||||
if (elem) {
|
if (elem) {
|
||||||
this.selectModelEntry(elem)
|
this.selectModelEntry(elem)
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
//this.highlightedModelEntry.parentElement.parentElement.scrollIntoView({block: 'nearest'})
|
//this.highlightedModelEntry.parentElement.parentElement.scrollIntoView({block: 'nearest'})
|
||||||
this.highlightedModelEntry.closest('.model-list').scrollTop = 0
|
this.highlightedModelEntry.closest(".model-list").scrollTop = 0
|
||||||
}
|
}
|
||||||
this.modelFilter.select()
|
this.modelFilter.select()
|
||||||
}
|
}
|
||||||
@ -178,13 +183,13 @@ class ModelDropdown
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectFirstFile() {
|
selectFirstFile() {
|
||||||
this.selectModelEntry(this.modelList.querySelector('.model-file'))
|
this.selectModelEntry(this.modelList.querySelector(".model-file"))
|
||||||
this.highlightedModelEntry.scrollIntoView({block: 'nearest'})
|
this.highlightedModelEntry.scrollIntoView({ block: "nearest" })
|
||||||
this.modelFilter.select()
|
this.modelFilter.select()
|
||||||
}
|
}
|
||||||
|
|
||||||
selectLastFile() {
|
selectLastFile() {
|
||||||
const elems = this.modelList.querySelectorAll('.model-file:last-child')
|
const elems = this.modelList.querySelectorAll(".model-file:last-child")
|
||||||
this.selectModelEntry(elems[elems.length - 1])
|
this.selectModelEntry(elems[elems.length - 1])
|
||||||
this.modelFilter.select()
|
this.modelFilter.select()
|
||||||
}
|
}
|
||||||
@ -198,57 +203,57 @@ class ModelDropdown
|
|||||||
}
|
}
|
||||||
|
|
||||||
validEntrySelected() {
|
validEntrySelected() {
|
||||||
return (this.modelNoResult.style.display === 'none')
|
return this.modelNoResult.style.display === "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
processKey(e) {
|
processKey(e) {
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case 'Escape':
|
case "Escape":
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.resetSelection()
|
this.resetSelection()
|
||||||
break
|
break
|
||||||
case 'Enter':
|
case "Enter":
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (this.validEntrySelected()) {
|
if (this.validEntrySelected()) {
|
||||||
if (this.modelList.style.display != 'block') {
|
if (this.modelList.style.display != "block") {
|
||||||
this.showModelList()
|
this.showModelList()
|
||||||
}
|
} else {
|
||||||
else
|
this.saveCurrentSelection(
|
||||||
{
|
this.highlightedModelEntry,
|
||||||
this.saveCurrentSelection(this.highlightedModelEntry, this.highlightedModelEntry.innerText, this.highlightedModelEntry.dataset.path)
|
this.highlightedModelEntry.innerText,
|
||||||
|
this.highlightedModelEntry.dataset.path
|
||||||
|
)
|
||||||
this.hideModelList()
|
this.hideModelList()
|
||||||
this.showAllEntries()
|
this.showAllEntries()
|
||||||
}
|
}
|
||||||
this.modelFilter.focus()
|
this.modelFilter.focus()
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
this.resetSelection()
|
this.resetSelection()
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'ArrowUp':
|
case "ArrowUp":
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (this.validEntrySelected()) {
|
if (this.validEntrySelected()) {
|
||||||
this.selectPreviousFile()
|
this.selectPreviousFile()
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'ArrowDown':
|
case "ArrowDown":
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (this.validEntrySelected()) {
|
if (this.validEntrySelected()) {
|
||||||
this.selectNextFile()
|
this.selectNextFile()
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'ArrowLeft':
|
case "ArrowLeft":
|
||||||
if (this.modelList.style.display != 'block') {
|
if (this.modelList.style.display != "block") {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'ArrowRight':
|
case "ArrowRight":
|
||||||
if (this.modelList.style.display != 'block') {
|
if (this.modelList.style.display != "block") {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'PageUp':
|
case "PageUp":
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (this.validEntrySelected()) {
|
if (this.validEntrySelected()) {
|
||||||
this.selectPreviousFile()
|
this.selectPreviousFile()
|
||||||
@ -261,7 +266,7 @@ class ModelDropdown
|
|||||||
this.selectPreviousFile()
|
this.selectPreviousFile()
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'PageDown':
|
case "PageDown":
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (this.validEntrySelected()) {
|
if (this.validEntrySelected()) {
|
||||||
this.selectNextFile()
|
this.selectNextFile()
|
||||||
@ -274,7 +279,7 @@ class ModelDropdown
|
|||||||
this.selectNextFile()
|
this.selectNextFile()
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'Home':
|
case "Home":
|
||||||
//if (this.modelList.style.display != 'block') {
|
//if (this.modelList.style.display != 'block') {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (this.validEntrySelected()) {
|
if (this.validEntrySelected()) {
|
||||||
@ -282,7 +287,7 @@ class ModelDropdown
|
|||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
break
|
break
|
||||||
case 'End':
|
case "End":
|
||||||
//if (this.modelList.style.display != 'block') {
|
//if (this.modelList.style.display != 'block') {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (this.validEntrySelected()) {
|
if (this.validEntrySelected()) {
|
||||||
@ -301,29 +306,27 @@ class ModelDropdown
|
|||||||
}
|
}
|
||||||
|
|
||||||
showModelList() {
|
showModelList() {
|
||||||
this.modelList.style.display = 'block'
|
this.modelList.style.display = "block"
|
||||||
this.selectEntry()
|
this.selectEntry()
|
||||||
this.showAllEntries()
|
this.showAllEntries()
|
||||||
//this.modelFilter.value = ''
|
//this.modelFilter.value = ''
|
||||||
this.modelFilter.select() // preselect the entire string so user can just start typing.
|
this.modelFilter.select() // preselect the entire string so user can just start typing.
|
||||||
this.modelFilter.focus()
|
this.modelFilter.focus()
|
||||||
this.modelFilter.style.cursor = 'auto'
|
this.modelFilter.style.cursor = "auto"
|
||||||
}
|
}
|
||||||
|
|
||||||
hideModelList() {
|
hideModelList() {
|
||||||
this.modelList.style.display = 'none'
|
this.modelList.style.display = "none"
|
||||||
this.modelFilter.value = this.currentSelection.value
|
this.modelFilter.value = this.currentSelection.value
|
||||||
this.modelFilter.style.cursor = ''
|
this.modelFilter.style.cursor = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleModelList(e) {
|
toggleModelList(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (!this.modelFilter.disabled) {
|
if (!this.modelFilter.disabled) {
|
||||||
if (this.modelList.style.display != 'block') {
|
if (this.modelList.style.display != "block") {
|
||||||
this.showModelList()
|
this.showModelList()
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
this.hideModelList()
|
this.hideModelList()
|
||||||
this.modelFilter.select()
|
this.modelFilter.select()
|
||||||
}
|
}
|
||||||
@ -332,13 +335,13 @@ class ModelDropdown
|
|||||||
|
|
||||||
selectEntry(path) {
|
selectEntry(path) {
|
||||||
if (path !== undefined) {
|
if (path !== undefined) {
|
||||||
const entries = this.modelElements;
|
const entries = this.modelElements
|
||||||
|
|
||||||
for (const elem of entries) {
|
for (const elem of entries) {
|
||||||
if (elem.dataset.path == path) {
|
if (elem.dataset.path == path) {
|
||||||
this.saveCurrentSelection(elem, elem.innerText, elem.dataset.path)
|
this.saveCurrentSelection(elem, elem.innerText, elem.dataset.path)
|
||||||
this.highlightedModelEntry = elem
|
this.highlightedModelEntry = elem
|
||||||
elem.scrollIntoView({block: 'nearest'})
|
elem.scrollIntoView({ block: "nearest" })
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,14 +350,12 @@ class ModelDropdown
|
|||||||
if (this.currentSelection.elem !== undefined) {
|
if (this.currentSelection.elem !== undefined) {
|
||||||
// select the previous element
|
// select the previous element
|
||||||
if (this.highlightedModelEntry !== undefined && this.highlightedModelEntry != this.currentSelection.elem) {
|
if (this.highlightedModelEntry !== undefined && this.highlightedModelEntry != this.currentSelection.elem) {
|
||||||
this.highlightedModelEntry.classList.remove('selected')
|
this.highlightedModelEntry.classList.remove("selected")
|
||||||
}
|
}
|
||||||
this.currentSelection.elem.classList.add('selected')
|
this.currentSelection.elem.classList.add("selected")
|
||||||
this.highlightedModelEntry = this.currentSelection.elem
|
this.highlightedModelEntry = this.currentSelection.elem
|
||||||
this.currentSelection.elem.scrollIntoView({block: 'nearest'})
|
this.currentSelection.elem.scrollIntoView({ block: "nearest" })
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
this.selectFirstFile()
|
this.selectFirstFile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -362,28 +363,28 @@ class ModelDropdown
|
|||||||
highlightModelAtPosition(e) {
|
highlightModelAtPosition(e) {
|
||||||
let elem = document.elementFromPoint(e.clientX, e.clientY)
|
let elem = document.elementFromPoint(e.clientX, e.clientY)
|
||||||
|
|
||||||
if (elem.classList.contains('model-file')) {
|
if (elem.classList.contains("model-file")) {
|
||||||
this.highlightModel(elem)
|
this.highlightModel(elem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightModel(elem) {
|
highlightModel(elem) {
|
||||||
if (elem.classList.contains('model-file')) {
|
if (elem.classList.contains("model-file")) {
|
||||||
if (this.highlightedModelEntry !== undefined && this.highlightedModelEntry != elem) {
|
if (this.highlightedModelEntry !== undefined && this.highlightedModelEntry != elem) {
|
||||||
this.highlightedModelEntry.classList.remove('selected')
|
this.highlightedModelEntry.classList.remove("selected")
|
||||||
}
|
}
|
||||||
elem.classList.add('selected')
|
elem.classList.add("selected")
|
||||||
this.highlightedModelEntry = elem
|
this.highlightedModelEntry = elem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showAllEntries() {
|
showAllEntries() {
|
||||||
this.modelList.querySelectorAll('li').forEach(function(li) {
|
this.modelList.querySelectorAll("li").forEach(function(li) {
|
||||||
if (li.id !== 'model-no-result') {
|
if (li.id !== "model-no-result") {
|
||||||
li.style.display = 'list-item'
|
li.style.display = "list-item"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.modelNoResult.style.display = 'none'
|
this.modelNoResult.style.display = "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
filterList(e) {
|
filterList(e) {
|
||||||
@ -391,37 +392,35 @@ class ModelDropdown
|
|||||||
let found = false
|
let found = false
|
||||||
let showAllChildren = false
|
let showAllChildren = false
|
||||||
|
|
||||||
this.modelList.querySelectorAll('li').forEach(function(li) {
|
this.modelList.querySelectorAll("li").forEach(function(li) {
|
||||||
if (li.classList.contains('model-folder')) {
|
if (li.classList.contains("model-folder")) {
|
||||||
showAllChildren = false
|
showAllChildren = false
|
||||||
}
|
}
|
||||||
if (filter == '') {
|
if (filter == "") {
|
||||||
li.style.display = 'list-item'
|
li.style.display = "list-item"
|
||||||
found = true
|
found = true
|
||||||
} else if (showAllChildren || li.textContent.toLowerCase().match(filter)) {
|
} else if (showAllChildren || li.textContent.toLowerCase().match(filter)) {
|
||||||
li.style.display = 'list-item'
|
li.style.display = "list-item"
|
||||||
if (li.classList.contains('model-folder') && li.firstChild.textContent.toLowerCase().match(filter)) {
|
if (li.classList.contains("model-folder") && li.firstChild.textContent.toLowerCase().match(filter)) {
|
||||||
showAllChildren = true
|
showAllChildren = true
|
||||||
}
|
}
|
||||||
found = true
|
found = true
|
||||||
} else {
|
} else {
|
||||||
li.style.display = 'none'
|
li.style.display = "none"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
this.modelResult.style.display = 'list-item'
|
this.modelResult.style.display = "list-item"
|
||||||
this.modelNoResult.style.display = 'none'
|
this.modelNoResult.style.display = "none"
|
||||||
const elem = this.getNextVisibleSibling(this.modelList.querySelector('.model-file'))
|
const elem = this.getNextVisibleSibling(this.modelList.querySelector(".model-file"))
|
||||||
this.highlightModel(elem)
|
this.highlightModel(elem)
|
||||||
elem.scrollIntoView({block: 'nearest'})
|
elem.scrollIntoView({ block: "nearest" })
|
||||||
|
} else {
|
||||||
|
this.modelResult.style.display = "none"
|
||||||
|
this.modelNoResult.style.display = "list-item"
|
||||||
}
|
}
|
||||||
else
|
this.modelList.style.display = "block"
|
||||||
{
|
|
||||||
this.modelResult.style.display = 'none'
|
|
||||||
this.modelNoResult.style.display = 'list-item'
|
|
||||||
}
|
|
||||||
this.modelList.style.display = 'block'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MODEL LOADER */
|
/* MODEL LOADER */
|
||||||
@ -458,13 +457,13 @@ class ModelDropdown
|
|||||||
* @param {Array<string>} models
|
* @param {Array<string>} models
|
||||||
*/
|
*/
|
||||||
sortStringArray(models) {
|
sortStringArray(models) {
|
||||||
models.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }))
|
models.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }))
|
||||||
}
|
}
|
||||||
|
|
||||||
populateModels() {
|
populateModels() {
|
||||||
this.activeModel = this.modelFilter.dataset.path
|
this.activeModel = this.modelFilter.dataset.path
|
||||||
|
|
||||||
this.currentSelection = { elem: undefined, value: '', path: ''}
|
this.currentSelection = { elem: undefined, value: "", path: "" }
|
||||||
this.highlightedModelEntry = undefined
|
this.highlightedModelEntry = undefined
|
||||||
this.flatModelList = []
|
this.flatModelList = []
|
||||||
|
|
||||||
@ -478,39 +477,39 @@ class ModelDropdown
|
|||||||
createDropdown() {
|
createDropdown() {
|
||||||
// create dropdown entries
|
// create dropdown entries
|
||||||
let rootModelList = this.createRootModelList(this.inputModels)
|
let rootModelList = this.createRootModelList(this.inputModels)
|
||||||
this.modelFilter.insertAdjacentElement('afterend', rootModelList)
|
this.modelFilter.insertAdjacentElement("afterend", rootModelList)
|
||||||
this.modelFilter.insertAdjacentElement(
|
this.modelFilter.insertAdjacentElement(
|
||||||
'afterend',
|
"afterend",
|
||||||
createElement(
|
createElement("i", { id: `${this.modelFilter.id}-model-filter-arrow` }, [
|
||||||
'i',
|
"model-selector-arrow",
|
||||||
{ id: `${this.modelFilter.id}-model-filter-arrow` },
|
"fa-solid",
|
||||||
['model-selector-arrow', 'fa-solid', 'fa-angle-down'],
|
"fa-angle-down"
|
||||||
),
|
])
|
||||||
)
|
)
|
||||||
this.modelFilter.classList.add('model-selector')
|
this.modelFilter.classList.add("model-selector")
|
||||||
this.modelFilterArrow = document.querySelector(`#${this.modelFilter.id}-model-filter-arrow`)
|
this.modelFilterArrow = document.querySelector(`#${this.modelFilter.id}-model-filter-arrow`)
|
||||||
if (this.modelFilterArrow) {
|
if (this.modelFilterArrow) {
|
||||||
this.modelFilterArrow.style.color = this.modelFilter.disabled ? 'dimgray' : ''
|
this.modelFilterArrow.style.color = this.modelFilter.disabled ? "dimgray" : ""
|
||||||
}
|
}
|
||||||
this.modelList = document.querySelector(`#${this.modelFilter.id}-model-list`)
|
this.modelList = document.querySelector(`#${this.modelFilter.id}-model-list`)
|
||||||
this.modelResult = document.querySelector(`#${this.modelFilter.id}-model-result`)
|
this.modelResult = document.querySelector(`#${this.modelFilter.id}-model-result`)
|
||||||
this.modelNoResult = document.querySelector(`#${this.modelFilter.id}-model-no-result`)
|
this.modelNoResult = document.querySelector(`#${this.modelFilter.id}-model-no-result`)
|
||||||
|
|
||||||
if (this.modelFilterInitialized !== true) {
|
if (this.modelFilterInitialized !== true) {
|
||||||
this.modelFilter.addEventListener('input', this.bind(this.filterList, this))
|
this.modelFilter.addEventListener("input", this.bind(this.filterList, this))
|
||||||
this.modelFilter.addEventListener('focus', this.bind(this.modelListFocus, this))
|
this.modelFilter.addEventListener("focus", this.bind(this.modelListFocus, this))
|
||||||
this.modelFilter.addEventListener('blur', this.bind(this.hideModelList, this))
|
this.modelFilter.addEventListener("blur", this.bind(this.hideModelList, this))
|
||||||
this.modelFilter.addEventListener('click', this.bind(this.showModelList, this))
|
this.modelFilter.addEventListener("click", this.bind(this.showModelList, this))
|
||||||
this.modelFilter.addEventListener('keydown', this.bind(this.processKey, this))
|
this.modelFilter.addEventListener("keydown", this.bind(this.processKey, this))
|
||||||
|
|
||||||
this.modelFilterInitialized = true
|
this.modelFilterInitialized = true
|
||||||
}
|
}
|
||||||
this.modelFilterArrow.addEventListener('mousedown', this.bind(this.toggleModelList, this))
|
this.modelFilterArrow.addEventListener("mousedown", this.bind(this.toggleModelList, this))
|
||||||
this.modelList.addEventListener('mousemove', this.bind(this.highlightModelAtPosition, this))
|
this.modelList.addEventListener("mousemove", this.bind(this.highlightModelAtPosition, this))
|
||||||
this.modelList.addEventListener('mousedown', this.bind(this.processClick, this))
|
this.modelList.addEventListener("mousedown", this.bind(this.processClick, this))
|
||||||
|
|
||||||
let mf = this.modelFilter
|
let mf = this.modelFilter
|
||||||
this.modelFilter.addEventListener('focus', function() {
|
this.modelFilter.addEventListener("focus", function() {
|
||||||
let modelFilterStyle = window.getComputedStyle(mf)
|
let modelFilterStyle = window.getComputedStyle(mf)
|
||||||
rootModelList.style.minWidth = modelFilterStyle.width
|
rootModelList.style.minWidth = modelFilterStyle.width
|
||||||
})
|
})
|
||||||
@ -525,69 +524,57 @@ class ModelDropdown
|
|||||||
* @returns {HTMLElement}
|
* @returns {HTMLElement}
|
||||||
*/
|
*/
|
||||||
createModelNodeList(folderName, modelTree, isRootFolder) {
|
createModelNodeList(folderName, modelTree, isRootFolder) {
|
||||||
const listElement = createElement('ul')
|
const listElement = createElement("ul")
|
||||||
|
|
||||||
const foldersMap = new Map()
|
const foldersMap = new Map()
|
||||||
const modelsMap = new Map()
|
const modelsMap = new Map()
|
||||||
|
|
||||||
modelTree.forEach(model => {
|
modelTree.forEach((model) => {
|
||||||
if (Array.isArray(model)) {
|
if (Array.isArray(model)) {
|
||||||
const [childFolderName, childModels] = model
|
const [childFolderName, childModels] = model
|
||||||
foldersMap.set(
|
foldersMap.set(
|
||||||
childFolderName,
|
childFolderName,
|
||||||
this.createModelNodeList(
|
this.createModelNodeList(`${folderName || ""}/${childFolderName}`, childModels, false)
|
||||||
`${folderName || ''}/${childFolderName}`,
|
|
||||||
childModels,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
const classes = ['model-file']
|
const classes = ["model-file"]
|
||||||
if (isRootFolder) {
|
if (isRootFolder) {
|
||||||
classes.push('in-root-folder')
|
classes.push("in-root-folder")
|
||||||
}
|
}
|
||||||
// Remove the leading slash from the model path
|
// Remove the leading slash from the model path
|
||||||
const fullPath = folderName ? `${folderName.substring(1)}/${model}` : model
|
const fullPath = folderName ? `${folderName.substring(1)}/${model}` : model
|
||||||
modelsMap.set(
|
modelsMap.set(
|
||||||
model,
|
model,
|
||||||
createElement(
|
createElement("li", { "data-path": fullPath }, classes, [
|
||||||
'li',
|
createElement("i", undefined, ["fa-regular", "fa-file", "icon"]),
|
||||||
{ 'data-path': fullPath },
|
model
|
||||||
classes,
|
])
|
||||||
[
|
|
||||||
createElement('i', undefined, ['fa-regular', 'fa-file', 'icon']),
|
|
||||||
model,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const childFolderNames = Array.from(foldersMap.keys())
|
const childFolderNames = Array.from(foldersMap.keys())
|
||||||
this.sortStringArray(childFolderNames)
|
this.sortStringArray(childFolderNames)
|
||||||
const folderElements = childFolderNames.map(name => foldersMap.get(name))
|
const folderElements = childFolderNames.map((name) => foldersMap.get(name))
|
||||||
|
|
||||||
const modelNames = Array.from(modelsMap.keys())
|
const modelNames = Array.from(modelsMap.keys())
|
||||||
this.sortStringArray(modelNames)
|
this.sortStringArray(modelNames)
|
||||||
const modelElements = modelNames.map(name => modelsMap.get(name))
|
const modelElements = modelNames.map((name) => modelsMap.get(name))
|
||||||
|
|
||||||
if (modelElements.length && folderName) {
|
if (modelElements.length && folderName) {
|
||||||
listElement.appendChild(
|
listElement.appendChild(
|
||||||
createElement(
|
createElement(
|
||||||
'li',
|
"li",
|
||||||
undefined,
|
undefined,
|
||||||
['model-folder'],
|
["model-folder"],
|
||||||
[
|
[createElement("i", undefined, ["fa-regular", "fa-folder-open", "icon"]), folderName.substring(1)]
|
||||||
createElement('i', undefined, ['fa-regular', 'fa-folder-open', 'icon']),
|
|
||||||
folderName.substring(1),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// const allModelElements = isRootFolder ? [...folderElements, ...modelElements] : [...modelElements, ...folderElements]
|
// const allModelElements = isRootFolder ? [...folderElements, ...modelElements] : [...modelElements, ...folderElements]
|
||||||
const allModelElements = [...modelElements, ...folderElements]
|
const allModelElements = [...modelElements, ...folderElements]
|
||||||
allModelElements.forEach(e => listElement.appendChild(e))
|
allModelElements.forEach((e) => listElement.appendChild(e))
|
||||||
return listElement
|
return listElement
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,37 +583,21 @@ class ModelDropdown
|
|||||||
* @returns {HTMLElement}
|
* @returns {HTMLElement}
|
||||||
*/
|
*/
|
||||||
createRootModelList(modelTree) {
|
createRootModelList(modelTree) {
|
||||||
const rootList = createElement(
|
const rootList = createElement("ul", { id: `${this.modelFilter.id}-model-list` }, ["model-list"])
|
||||||
'ul',
|
|
||||||
{ id: `${this.modelFilter.id}-model-list` },
|
|
||||||
['model-list'],
|
|
||||||
)
|
|
||||||
rootList.appendChild(
|
rootList.appendChild(
|
||||||
createElement(
|
createElement("li", { id: `${this.modelFilter.id}-model-no-result` }, ["model-no-result"], "No result")
|
||||||
'li',
|
|
||||||
{ id: `${this.modelFilter.id}-model-no-result` },
|
|
||||||
['model-no-result'],
|
|
||||||
'No result'
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (this.noneEntry) {
|
if (this.noneEntry) {
|
||||||
rootList.appendChild(
|
rootList.appendChild(
|
||||||
createElement(
|
createElement("li", { "data-path": "" }, ["model-file", "in-root-folder"], this.noneEntry)
|
||||||
'li',
|
|
||||||
{ 'data-path': '' },
|
|
||||||
['model-file', 'in-root-folder'],
|
|
||||||
this.noneEntry,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelTree.length > 0) {
|
if (modelTree.length > 0) {
|
||||||
const containerListItem = createElement(
|
const containerListItem = createElement("li", { id: `${this.modelFilter.id}-model-result` }, [
|
||||||
'li',
|
"model-result"
|
||||||
{ id: `${this.modelFilter.id}-model-result` },
|
])
|
||||||
['model-result'],
|
|
||||||
)
|
|
||||||
//console.log(containerListItem)
|
//console.log(containerListItem)
|
||||||
containerListItem.appendChild(this.createModelNodeList(undefined, modelTree, true))
|
containerListItem.appendChild(this.createModelNodeList(undefined, modelTree, true))
|
||||||
rootList.appendChild(containerListItem)
|
rootList.appendChild(containerListItem)
|
||||||
@ -640,13 +611,16 @@ class ModelDropdown
|
|||||||
async function getModels() {
|
async function getModels() {
|
||||||
try {
|
try {
|
||||||
modelsCache = await SD.getModels()
|
modelsCache = await SD.getModels()
|
||||||
modelsOptions = modelsCache['options']
|
modelsOptions = modelsCache["options"]
|
||||||
if ("scan-error" in modelsCache) {
|
if ("scan-error" in modelsCache) {
|
||||||
// let previewPane = document.getElementById('tab-content-wrapper')
|
// let previewPane = document.getElementById('tab-content-wrapper')
|
||||||
let previewPane = document.getElementById('preview')
|
let previewPane = document.getElementById("preview")
|
||||||
previewPane.style.background = "red"
|
previewPane.style.background = "red"
|
||||||
previewPane.style.textAlign = "center"
|
previewPane.style.textAlign = "center"
|
||||||
previewPane.innerHTML = '<H1>🔥Malware alert!🔥</H1><h2>The file <i>' + modelsCache['scan-error'] + '</i> in your <tt>models/stable-diffusion</tt> folder is probably malware infected.</h2><h2>Please delete this file from the folder before proceeding!</h2>After deleting the file, reload this page.<br><br><button onClick="window.location.reload();">Reload Page</button>'
|
previewPane.innerHTML =
|
||||||
|
"<H1>🔥Malware alert!🔥</H1><h2>The file <i>" +
|
||||||
|
modelsCache["scan-error"] +
|
||||||
|
'</i> in your <tt>models/stable-diffusion</tt> folder is probably malware infected.</h2><h2>Please delete this file from the folder before proceeding!</h2>After deleting the file, reload this page.<br><br><button onClick="window.location.reload();">Reload Page</button>'
|
||||||
makeImageBtn.disabled = true
|
makeImageBtn.disabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,11 +641,11 @@ async function getModels() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// notify ModelDropdown objects to refresh
|
// notify ModelDropdown objects to refresh
|
||||||
document.dispatchEvent(new Event('refreshModels'))
|
document.dispatchEvent(new Event("refreshModels"))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('get models error', e)
|
console.log("get models error", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reload models button
|
// reload models button
|
||||||
document.querySelector('#reload-models').addEventListener('click', getModels)
|
document.querySelector("#reload-models").addEventListener("click", getModels)
|
||||||
|
@ -1,28 +1,32 @@
|
|||||||
const themeField = document.getElementById("theme");
|
const themeField = document.getElementById("theme")
|
||||||
var DEFAULT_THEME = {};
|
var DEFAULT_THEME = {}
|
||||||
var THEMES = []; // initialized in initTheme from data in css
|
var THEMES = [] // initialized in initTheme from data in css
|
||||||
|
|
||||||
function getThemeName(theme) {
|
function getThemeName(theme) {
|
||||||
theme = theme.replace("theme-", "");
|
theme = theme.replace("theme-", "")
|
||||||
theme = theme.split("-").map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
theme = theme
|
||||||
return theme;
|
.split("-")
|
||||||
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
|
.join(" ")
|
||||||
|
return theme
|
||||||
}
|
}
|
||||||
// init themefield
|
// init themefield
|
||||||
function initTheme() {
|
function initTheme() {
|
||||||
Array.from(document.styleSheets)
|
Array.from(document.styleSheets)
|
||||||
.filter(sheet => sheet.href?.startsWith(window.location.origin))
|
.filter((sheet) => sheet.href?.startsWith(window.location.origin))
|
||||||
.flatMap(sheet => Array.from(sheet.cssRules))
|
.flatMap((sheet) => Array.from(sheet.cssRules))
|
||||||
.forEach(rule => {
|
.forEach((rule) => {
|
||||||
var selector = rule.selectorText;
|
var selector = rule.selectorText
|
||||||
if (selector && selector.startsWith(".theme-") && !selector.includes(" ")) {
|
if (selector && selector.startsWith(".theme-") && !selector.includes(" ")) {
|
||||||
if (DEFAULT_THEME) { // re-add props that dont change (css needs this so they update correctly)
|
if (DEFAULT_THEME) {
|
||||||
|
// re-add props that dont change (css needs this so they update correctly)
|
||||||
Array.from(DEFAULT_THEME.rule.style)
|
Array.from(DEFAULT_THEME.rule.style)
|
||||||
.filter(cssVariable => !Array.from(rule.style).includes(cssVariable))
|
.filter((cssVariable) => !Array.from(rule.style).includes(cssVariable))
|
||||||
.forEach(cssVariable => {
|
.forEach((cssVariable) => {
|
||||||
rule.style.setProperty(cssVariable, DEFAULT_THEME.rule.style.getPropertyValue(cssVariable));
|
rule.style.setProperty(cssVariable, DEFAULT_THEME.rule.style.getPropertyValue(cssVariable))
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
var theme_key = selector.substring(1);
|
var theme_key = selector.substring(1)
|
||||||
THEMES.push({
|
THEMES.push({
|
||||||
key: theme_key,
|
key: theme_key,
|
||||||
name: getThemeName(theme_key),
|
name: getThemeName(theme_key),
|
||||||
@ -34,49 +38,48 @@ function initTheme() {
|
|||||||
key: "theme-default",
|
key: "theme-default",
|
||||||
name: "Default",
|
name: "Default",
|
||||||
rule: rule
|
rule: rule
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
})
|
||||||
THEMES.forEach(theme => {
|
|
||||||
var new_option = document.createElement("option");
|
|
||||||
new_option.setAttribute("value", theme.key);
|
|
||||||
new_option.innerText = theme.name;
|
|
||||||
themeField.appendChild(new_option);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
THEMES.forEach((theme) => {
|
||||||
|
var new_option = document.createElement("option")
|
||||||
|
new_option.setAttribute("value", theme.key)
|
||||||
|
new_option.innerText = theme.name
|
||||||
|
themeField.appendChild(new_option)
|
||||||
|
})
|
||||||
|
|
||||||
// setup the style transitions a second after app initializes, so initial style is instant
|
// setup the style transitions a second after app initializes, so initial style is instant
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
var body = document.querySelector("body");
|
var body = document.querySelector("body")
|
||||||
var style = document.createElement('style');
|
var style = document.createElement("style")
|
||||||
style.innerHTML = "* { transition: background 0.5s, color 0.5s, background-color 0.5s; }";
|
style.innerHTML = "* { transition: background 0.5s, color 0.5s, background-color 0.5s; }"
|
||||||
body.appendChild(style);
|
body.appendChild(style)
|
||||||
}, 1000);
|
}, 1000)
|
||||||
}
|
}
|
||||||
initTheme();
|
initTheme()
|
||||||
|
|
||||||
function themeFieldChanged() {
|
function themeFieldChanged() {
|
||||||
var theme_key = themeField.value;
|
var theme_key = themeField.value
|
||||||
|
|
||||||
var body = document.querySelector("body");
|
var body = document.querySelector("body")
|
||||||
body.classList.remove(...THEMES.map(theme => theme.key));
|
body.classList.remove(...THEMES.map((theme) => theme.key))
|
||||||
body.classList.add(theme_key);
|
body.classList.add(theme_key)
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
body.style = "";
|
body.style = ""
|
||||||
var theme = THEMES.find(t => t.key == theme_key);
|
var theme = THEMES.find((t) => t.key == theme_key)
|
||||||
let borderColor = undefined
|
let borderColor = undefined
|
||||||
if (theme) {
|
if (theme) {
|
||||||
borderColor = theme.rule.style.getPropertyValue('--input-border-color').trim()
|
borderColor = theme.rule.style.getPropertyValue("--input-border-color").trim()
|
||||||
if (!borderColor.startsWith('#')) {
|
if (!borderColor.startsWith("#")) {
|
||||||
borderColor = theme.rule.style.getPropertyValue('--theme-color-fallback')
|
borderColor = theme.rule.style.getPropertyValue("--theme-color-fallback")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
borderColor = DEFAULT_THEME.rule.style.getPropertyValue('--theme-color-fallback')
|
borderColor = DEFAULT_THEME.rule.style.getPropertyValue("--theme-color-fallback")
|
||||||
}
|
}
|
||||||
document.querySelector('meta[name="theme-color"]').setAttribute("content", borderColor)
|
document.querySelector('meta[name="theme-color"]').setAttribute("content", borderColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
themeField.addEventListener('change', themeFieldChanged);
|
themeField.addEventListener("change", themeFieldChanged)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"use strict";
|
"use strict"
|
||||||
|
|
||||||
// https://gomakethings.com/finding-the-next-and-previous-sibling-elements-that-match-a-selector-with-vanilla-js/
|
// https://gomakethings.com/finding-the-next-and-previous-sibling-elements-that-match-a-selector-with-vanilla-js/
|
||||||
function getNextSibling(elem, selector) {
|
function getNextSibling(elem, selector) {
|
||||||
@ -20,32 +20,33 @@ function getNextSibling(elem, selector) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Panel Stuff */
|
/* Panel Stuff */
|
||||||
|
|
||||||
// true = open
|
// true = open
|
||||||
let COLLAPSIBLES_INITIALIZED = false;
|
let COLLAPSIBLES_INITIALIZED = false
|
||||||
const COLLAPSIBLES_KEY = "collapsibles";
|
const COLLAPSIBLES_KEY = "collapsibles"
|
||||||
const COLLAPSIBLE_PANELS = []; // filled in by createCollapsibles with all the elements matching .collapsible
|
const COLLAPSIBLE_PANELS = [] // filled in by createCollapsibles with all the elements matching .collapsible
|
||||||
|
|
||||||
// on-init call this for any panels that are marked open
|
// on-init call this for any panels that are marked open
|
||||||
function toggleCollapsible(element) {
|
function toggleCollapsible(element) {
|
||||||
const collapsibleHeader = element.querySelector(".collapsible");
|
const collapsibleHeader = element.querySelector(".collapsible")
|
||||||
const handle = element.querySelector(".collapsible-handle");
|
const handle = element.querySelector(".collapsible-handle")
|
||||||
collapsibleHeader.classList.toggle("active")
|
collapsibleHeader.classList.toggle("active")
|
||||||
let content = getNextSibling(collapsibleHeader, '.collapsible-content')
|
let content = getNextSibling(collapsibleHeader, ".collapsible-content")
|
||||||
if (!collapsibleHeader.classList.contains("active")) {
|
if (!collapsibleHeader.classList.contains("active")) {
|
||||||
content.style.display = "none"
|
content.style.display = "none"
|
||||||
if (handle != null) { // render results don't have a handle
|
if (handle != null) {
|
||||||
handle.innerHTML = '➕' // plus
|
// render results don't have a handle
|
||||||
|
handle.innerHTML = "➕" // plus
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
content.style.display = "block"
|
content.style.display = "block"
|
||||||
if (handle != null) { // render results don't have a handle
|
if (handle != null) {
|
||||||
handle.innerHTML = '➖' // minus
|
// render results don't have a handle
|
||||||
|
handle.innerHTML = "➖" // minus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.dispatchEvent(new CustomEvent('collapsibleClick', { detail: collapsibleHeader }))
|
document.dispatchEvent(new CustomEvent("collapsibleClick", { detail: collapsibleHeader }))
|
||||||
|
|
||||||
if (COLLAPSIBLES_INITIALIZED && COLLAPSIBLE_PANELS.includes(element)) {
|
if (COLLAPSIBLES_INITIALIZED && COLLAPSIBLE_PANELS.includes(element)) {
|
||||||
saveCollapsibles()
|
saveCollapsibles()
|
||||||
@ -54,7 +55,7 @@ function toggleCollapsible(element) {
|
|||||||
|
|
||||||
function saveCollapsibles() {
|
function saveCollapsibles() {
|
||||||
let values = {}
|
let values = {}
|
||||||
COLLAPSIBLE_PANELS.forEach(element => {
|
COLLAPSIBLE_PANELS.forEach((element) => {
|
||||||
let value = element.querySelector(".collapsible").className.indexOf("active") !== -1
|
let value = element.querySelector(".collapsible").className.indexOf("active") !== -1
|
||||||
values[element.id] = value
|
values[element.id] = value
|
||||||
})
|
})
|
||||||
@ -72,31 +73,31 @@ function createCollapsibles(node) {
|
|||||||
if (save && c.parentElement.id) {
|
if (save && c.parentElement.id) {
|
||||||
COLLAPSIBLE_PANELS.push(c.parentElement)
|
COLLAPSIBLE_PANELS.push(c.parentElement)
|
||||||
}
|
}
|
||||||
let handle = document.createElement('span')
|
let handle = document.createElement("span")
|
||||||
handle.className = 'collapsible-handle'
|
handle.className = "collapsible-handle"
|
||||||
|
|
||||||
if (c.classList.contains("active")) {
|
if (c.classList.contains("active")) {
|
||||||
handle.innerHTML = '➖' // minus
|
handle.innerHTML = "➖" // minus
|
||||||
} else {
|
} else {
|
||||||
handle.innerHTML = '➕' // plus
|
handle.innerHTML = "➕" // plus
|
||||||
}
|
}
|
||||||
c.insertBefore(handle, c.firstChild)
|
c.insertBefore(handle, c.firstChild)
|
||||||
|
|
||||||
c.addEventListener('click', function() {
|
c.addEventListener("click", function() {
|
||||||
toggleCollapsible(c.parentElement)
|
toggleCollapsible(c.parentElement)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if (save) {
|
if (save) {
|
||||||
let saved = localStorage.getItem(COLLAPSIBLES_KEY)
|
let saved = localStorage.getItem(COLLAPSIBLES_KEY)
|
||||||
if (!saved) {
|
if (!saved) {
|
||||||
saved = tryLoadOldCollapsibles();
|
saved = tryLoadOldCollapsibles()
|
||||||
}
|
}
|
||||||
if (!saved) {
|
if (!saved) {
|
||||||
saveCollapsibles()
|
saveCollapsibles()
|
||||||
saved = localStorage.getItem(COLLAPSIBLES_KEY)
|
saved = localStorage.getItem(COLLAPSIBLES_KEY)
|
||||||
}
|
}
|
||||||
let values = JSON.parse(saved)
|
let values = JSON.parse(saved)
|
||||||
COLLAPSIBLE_PANELS.forEach(element => {
|
COLLAPSIBLE_PANELS.forEach((element) => {
|
||||||
let value = element.querySelector(".collapsible").className.indexOf("active") !== -1
|
let value = element.querySelector(".collapsible").className.indexOf("active") !== -1
|
||||||
if (values[element.id] != value) {
|
if (values[element.id] != value) {
|
||||||
toggleCollapsible(element)
|
toggleCollapsible(element)
|
||||||
@ -108,24 +109,24 @@ function createCollapsibles(node) {
|
|||||||
|
|
||||||
function tryLoadOldCollapsibles() {
|
function tryLoadOldCollapsibles() {
|
||||||
const old_map = {
|
const old_map = {
|
||||||
"advancedPanelOpen": "editor-settings",
|
advancedPanelOpen: "editor-settings",
|
||||||
"modifiersPanelOpen": "editor-modifiers",
|
modifiersPanelOpen: "editor-modifiers",
|
||||||
"negativePromptPanelOpen": "editor-inputs-prompt"
|
negativePromptPanelOpen: "editor-inputs-prompt"
|
||||||
};
|
}
|
||||||
if (localStorage.getItem(Object.keys(old_map)[0])) {
|
if (localStorage.getItem(Object.keys(old_map)[0])) {
|
||||||
let result = {};
|
let result = {}
|
||||||
Object.keys(old_map).forEach(key => {
|
Object.keys(old_map).forEach((key) => {
|
||||||
const value = localStorage.getItem(key);
|
const value = localStorage.getItem(key)
|
||||||
if (value !== null) {
|
if (value !== null) {
|
||||||
result[old_map[key]] = (value == true || value == "true")
|
result[old_map[key]] = value == true || value == "true"
|
||||||
localStorage.removeItem(key)
|
localStorage.removeItem(key)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
result = JSON.stringify(result)
|
result = JSON.stringify(result)
|
||||||
localStorage.setItem(COLLAPSIBLES_KEY, result)
|
localStorage.setItem(COLLAPSIBLES_KEY, result)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function permute(arr) {
|
function permute(arr) {
|
||||||
@ -134,10 +135,12 @@ function permute(arr) {
|
|||||||
let n_permutations = Math.pow(2, n)
|
let n_permutations = Math.pow(2, n)
|
||||||
for (let i = 0; i < n_permutations; i++) {
|
for (let i = 0; i < n_permutations; i++) {
|
||||||
let perm = []
|
let perm = []
|
||||||
let mask = Number(i).toString(2).padStart(n, '0')
|
let mask = Number(i)
|
||||||
|
.toString(2)
|
||||||
|
.padStart(n, "0")
|
||||||
|
|
||||||
for (let idx = 0; idx < mask.length; idx++) {
|
for (let idx = 0; idx < mask.length; idx++) {
|
||||||
if (mask[idx] === '1' && arr[idx].trim() !== '') {
|
if (mask[idx] === "1" && arr[idx].trim() !== "") {
|
||||||
perm.push(arr[idx])
|
perm.push(arr[idx])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,22 +156,22 @@ function permute(arr) {
|
|||||||
// https://stackoverflow.com/a/8212878
|
// https://stackoverflow.com/a/8212878
|
||||||
function millisecondsToStr(milliseconds) {
|
function millisecondsToStr(milliseconds) {
|
||||||
function numberEnding(number) {
|
function numberEnding(number) {
|
||||||
return (number > 1) ? 's' : ''
|
return number > 1 ? "s" : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
let temp = Math.floor(milliseconds / 1000)
|
let temp = Math.floor(milliseconds / 1000)
|
||||||
let hours = Math.floor((temp %= 86400) / 3600)
|
let hours = Math.floor((temp %= 86400) / 3600)
|
||||||
let s = ''
|
let s = ""
|
||||||
if (hours) {
|
if (hours) {
|
||||||
s += hours + ' hour' + numberEnding(hours) + ' '
|
s += hours + " hour" + numberEnding(hours) + " "
|
||||||
}
|
}
|
||||||
let minutes = Math.floor((temp %= 3600) / 60)
|
let minutes = Math.floor((temp %= 3600) / 60)
|
||||||
if (minutes) {
|
if (minutes) {
|
||||||
s += minutes + ' minute' + numberEnding(minutes) + ' '
|
s += minutes + " minute" + numberEnding(minutes) + " "
|
||||||
}
|
}
|
||||||
let seconds = temp % 60
|
let seconds = temp % 60
|
||||||
if (!hours && minutes < 4 && seconds) {
|
if (!hours && minutes < 4 && seconds) {
|
||||||
s += seconds + ' second' + numberEnding(seconds)
|
s += seconds + " second" + numberEnding(seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
@ -176,101 +179,82 @@ function millisecondsToStr(milliseconds) {
|
|||||||
|
|
||||||
// https://rosettacode.org/wiki/Brace_expansion#JavaScript
|
// https://rosettacode.org/wiki/Brace_expansion#JavaScript
|
||||||
function BraceExpander() {
|
function BraceExpander() {
|
||||||
'use strict'
|
"use strict"
|
||||||
|
|
||||||
// Index of any closing brace matching the opening
|
// Index of any closing brace matching the opening
|
||||||
// brace at iPosn,
|
// brace at iPosn,
|
||||||
// with the indices of any immediately-enclosed commas.
|
// with the indices of any immediately-enclosed commas.
|
||||||
function bracePair(tkns, iPosn, iNest, lstCommas) {
|
function bracePair(tkns, iPosn, iNest, lstCommas) {
|
||||||
if (iPosn >= tkns.length || iPosn < 0) return null;
|
if (iPosn >= tkns.length || iPosn < 0) return null
|
||||||
|
|
||||||
let t = tkns[iPosn],
|
let t = tkns[iPosn],
|
||||||
n = (t === '{') ? (
|
n = t === "{" ? iNest + 1 : t === "}" ? iNest - 1 : iNest,
|
||||||
iNest + 1
|
lst = t === "," && iNest === 1 ? lstCommas.concat(iPosn) : lstCommas
|
||||||
) : (t === '}' ? (
|
|
||||||
iNest - 1
|
|
||||||
) : iNest),
|
|
||||||
lst = (t === ',' && iNest === 1) ? (
|
|
||||||
lstCommas.concat(iPosn)
|
|
||||||
) : lstCommas;
|
|
||||||
|
|
||||||
return n ? bracePair(tkns, iPosn + 1, n, lst) : {
|
return n
|
||||||
|
? bracePair(tkns, iPosn + 1, n, lst)
|
||||||
|
: {
|
||||||
close: iPosn,
|
close: iPosn,
|
||||||
commas: lst
|
commas: lst
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse of a SYNTAGM subtree
|
// Parse of a SYNTAGM subtree
|
||||||
function andTree(dctSofar, tkns) {
|
function andTree(dctSofar, tkns) {
|
||||||
if (!tkns.length) return [dctSofar, []];
|
if (!tkns.length) return [dctSofar, []]
|
||||||
|
|
||||||
let dctParse = dctSofar ? dctSofar : {
|
let dctParse = dctSofar
|
||||||
|
? dctSofar
|
||||||
|
: {
|
||||||
fn: and,
|
fn: and,
|
||||||
args: []
|
args: []
|
||||||
},
|
},
|
||||||
|
|
||||||
head = tkns[0],
|
head = tkns[0],
|
||||||
tail = head ? tkns.slice(1) : [],
|
tail = head ? tkns.slice(1) : [],
|
||||||
|
dctBrace = head === "{" ? bracePair(tkns, 0, 0, []) : null,
|
||||||
|
lstOR = dctBrace && dctBrace.close && dctBrace.commas.length ? splitAt(dctBrace.close + 1, tkns) : null
|
||||||
|
|
||||||
dctBrace = head === '{' ? bracePair(
|
return andTree(
|
||||||
tkns, 0, 0, []
|
{
|
||||||
) : null,
|
|
||||||
|
|
||||||
lstOR = dctBrace && (
|
|
||||||
dctBrace.close
|
|
||||||
) && dctBrace.commas.length ? (
|
|
||||||
splitAt(dctBrace.close + 1, tkns)
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
return andTree({
|
|
||||||
fn: and,
|
fn: and,
|
||||||
args: dctParse.args.concat(
|
args: dctParse.args.concat(lstOR ? orTree(dctParse, lstOR[0], dctBrace.commas) : head)
|
||||||
lstOR ? (
|
},
|
||||||
orTree(dctParse, lstOR[0], dctBrace.commas)
|
lstOR ? lstOR[1] : tail
|
||||||
) : head
|
|
||||||
)
|
)
|
||||||
}, lstOR ? (
|
|
||||||
lstOR[1]
|
|
||||||
) : tail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse of a PARADIGM subtree
|
// Parse of a PARADIGM subtree
|
||||||
function orTree(dctSofar, tkns, lstCommas) {
|
function orTree(dctSofar, tkns, lstCommas) {
|
||||||
if (!tkns.length) return [dctSofar, []];
|
if (!tkns.length) return [dctSofar, []]
|
||||||
let iLast = lstCommas.length;
|
let iLast = lstCommas.length
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fn: or,
|
fn: or,
|
||||||
args: splitsAt(
|
args: splitsAt(lstCommas, tkns)
|
||||||
lstCommas, tkns
|
.map(function(x, i) {
|
||||||
).map(function (x, i) {
|
let ts = x.slice(1, i === iLast ? -1 : void 0)
|
||||||
let ts = x.slice(
|
|
||||||
1, i === iLast ? (
|
|
||||||
-1
|
|
||||||
) : void 0
|
|
||||||
);
|
|
||||||
|
|
||||||
return ts.length ? ts : [''];
|
return ts.length ? ts : [""]
|
||||||
}).map(function (ts) {
|
|
||||||
return ts.length > 1 ? (
|
|
||||||
andTree(null, ts)[0]
|
|
||||||
) : ts[0];
|
|
||||||
})
|
})
|
||||||
};
|
.map(function(ts) {
|
||||||
|
return ts.length > 1 ? andTree(null, ts)[0] : ts[0]
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of unescaped braces and commas, and remaining strings
|
// List of unescaped braces and commas, and remaining strings
|
||||||
function tokens(str) {
|
function tokens(str) {
|
||||||
// Filter function excludes empty splitting artefacts
|
// Filter function excludes empty splitting artefacts
|
||||||
let toS = function(x) {
|
let toS = function(x) {
|
||||||
return x.toString();
|
return x.toString()
|
||||||
};
|
}
|
||||||
|
|
||||||
return str.split(/(\\\\)/).filter(toS).reduce(function (a, s) {
|
return str
|
||||||
return a.concat(s.charAt(0) === '\\' ? s : s.split(
|
.split(/(\\\\)/)
|
||||||
/(\\*[{,}])/
|
.filter(toS)
|
||||||
).filter(toS));
|
.reduce(function(a, s) {
|
||||||
}, []);
|
return a.concat(s.charAt(0) === "\\" ? s : s.split(/(\\*[{,}])/).filter(toS))
|
||||||
|
}, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
// PARSE TREE OPERATOR (1 of 2)
|
// PARSE TREE OPERATOR (1 of 2)
|
||||||
@ -278,76 +262,75 @@ function BraceExpander() {
|
|||||||
function and(args) {
|
function and(args) {
|
||||||
let lng = args.length,
|
let lng = args.length,
|
||||||
head = lng ? args[0] : null,
|
head = lng ? args[0] : null,
|
||||||
lstHead = "string" === typeof head ? (
|
lstHead = "string" === typeof head ? [head] : head
|
||||||
[head]
|
|
||||||
) : head;
|
|
||||||
|
|
||||||
return lng ? (
|
return lng
|
||||||
1 < lng ? lstHead.reduce(function (a, h) {
|
? 1 < lng
|
||||||
|
? lstHead.reduce(function(a, h) {
|
||||||
return a.concat(
|
return a.concat(
|
||||||
and(args.slice(1)).map(function(t) {
|
and(args.slice(1)).map(function(t) {
|
||||||
return h + t;
|
return h + t
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
}, []) : lstHead
|
}, [])
|
||||||
) : [];
|
: lstHead
|
||||||
|
: []
|
||||||
}
|
}
|
||||||
|
|
||||||
// PARSE TREE OPERATOR (2 of 2)
|
// PARSE TREE OPERATOR (2 of 2)
|
||||||
// Each option flattened
|
// Each option flattened
|
||||||
function or(args) {
|
function or(args) {
|
||||||
return args.reduce(function(a, b) {
|
return args.reduce(function(a, b) {
|
||||||
return a.concat(b);
|
return a.concat(b)
|
||||||
}, []);
|
}, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
// One list split into two (first sublist length n)
|
// One list split into two (first sublist length n)
|
||||||
function splitAt(n, lst) {
|
function splitAt(n, lst) {
|
||||||
return n < lst.length + 1 ? [
|
return n < lst.length + 1 ? [lst.slice(0, n), lst.slice(n)] : [lst, []]
|
||||||
lst.slice(0, n), lst.slice(n)
|
|
||||||
] : [lst, []];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// One list split into several (sublist lengths [n])
|
// One list split into several (sublist lengths [n])
|
||||||
function splitsAt(lstN, lst) {
|
function splitsAt(lstN, lst) {
|
||||||
return lstN.reduceRight(function (a, x) {
|
return lstN.reduceRight(
|
||||||
return splitAt(x, a[0]).concat(a.slice(1));
|
function(a, x) {
|
||||||
}, [lst]);
|
return splitAt(x, a[0]).concat(a.slice(1))
|
||||||
|
},
|
||||||
|
[lst]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value of the parse tree
|
// Value of the parse tree
|
||||||
function evaluated(e) {
|
function evaluated(e) {
|
||||||
return typeof e === 'string' ? e :
|
return typeof e === "string" ? e : e.fn(e.args.map(evaluated))
|
||||||
e.fn(e.args.map(evaluated));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON prettyprint (for parse tree, token list etc)
|
// JSON prettyprint (for parse tree, token list etc)
|
||||||
function pp(e) {
|
function pp(e) {
|
||||||
return JSON.stringify(e, function (k, v) {
|
return JSON.stringify(
|
||||||
return typeof v === 'function' ? (
|
e,
|
||||||
'[function ' + v.name + ']'
|
function(k, v) {
|
||||||
) : v;
|
return typeof v === "function" ? "[function " + v.name + "]" : v
|
||||||
}, 2)
|
},
|
||||||
|
2
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------- MAIN ------------------------
|
// ----------------------- MAIN ------------------------
|
||||||
|
|
||||||
// s -> [s]
|
// s -> [s]
|
||||||
this.expand = function(s) {
|
this.expand = function(s) {
|
||||||
// BRACE EXPRESSION PARSED
|
// BRACE EXPRESSION PARSED
|
||||||
let dctParse = andTree(null, tokens(s))[0];
|
let dctParse = andTree(null, tokens(s))[0]
|
||||||
|
|
||||||
// ABSTRACT SYNTAX TREE LOGGED
|
// ABSTRACT SYNTAX TREE LOGGED
|
||||||
// console.log(pp(dctParse));
|
// console.log(pp(dctParse));
|
||||||
|
|
||||||
// AST EVALUATED TO LIST OF STRINGS
|
// AST EVALUATED TO LIST OF STRINGS
|
||||||
return evaluated(dctParse);
|
return evaluated(dctParse)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Pause the execution of an async function until timer elapse.
|
/** Pause the execution of an async function until timer elapse.
|
||||||
* @Returns a promise that will resolve after the specified timeout.
|
* @Returns a promise that will resolve after the specified timeout.
|
||||||
*/
|
*/
|
||||||
@ -360,8 +343,8 @@ function asyncDelay(timeout) {
|
|||||||
function PromiseSource() {
|
function PromiseSource() {
|
||||||
const srcPromise = new Promise((resolve, reject) => {
|
const srcPromise = new Promise((resolve, reject) => {
|
||||||
Object.defineProperties(this, {
|
Object.defineProperties(this, {
|
||||||
resolve: { value: resolve, writable: false }
|
resolve: { value: resolve, writable: false },
|
||||||
, reject: { value: reject, writable: false }
|
reject: { value: reject, writable: false }
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
Object.defineProperties(this, {
|
Object.defineProperties(this, {
|
||||||
@ -399,7 +382,7 @@ function debounce (func, wait, immediate) {
|
|||||||
}
|
}
|
||||||
return function(...args) {
|
return function(...args) {
|
||||||
const callNow = Boolean(immediate && !timeout)
|
const callNow = Boolean(immediate && !timeout)
|
||||||
const context = this;
|
const context = this
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
}
|
}
|
||||||
@ -418,14 +401,14 @@ function debounce (func, wait, immediate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function preventNonNumericalInput(e) {
|
function preventNonNumericalInput(e) {
|
||||||
e = e || window.event;
|
e = e || window.event
|
||||||
let charCode = (typeof e.which == "undefined") ? e.keyCode : e.which;
|
let charCode = typeof e.which == "undefined" ? e.keyCode : e.which
|
||||||
let charStr = String.fromCharCode(charCode);
|
let charStr = String.fromCharCode(charCode)
|
||||||
let re = e.target.getAttribute('pattern') || '^[0-9]+$'
|
let re = e.target.getAttribute("pattern") || "^[0-9]+$"
|
||||||
re = new RegExp(re)
|
re = new RegExp(re)
|
||||||
|
|
||||||
if (!charStr.match(re)) {
|
if (!charStr.match(re)) {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,15 +417,15 @@ function preventNonNumericalInput(e) {
|
|||||||
* @Notes Allows unit testing and use of the engine outside of a browser.
|
* @Notes Allows unit testing and use of the engine outside of a browser.
|
||||||
*/
|
*/
|
||||||
function getGlobal() {
|
function getGlobal() {
|
||||||
if (typeof globalThis === 'object') {
|
if (typeof globalThis === "object") {
|
||||||
return globalThis
|
return globalThis
|
||||||
} else if (typeof global === 'object') {
|
} else if (typeof global === "object") {
|
||||||
return global
|
return global
|
||||||
} else if (typeof self === 'object') {
|
} else if (typeof self === "object") {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return Function('return this')()
|
return Function("return this")()
|
||||||
} catch {
|
} catch {
|
||||||
// If the Function constructor fails, we're in a browser with eval disabled by CSP headers.
|
// If the Function constructor fails, we're in a browser with eval disabled by CSP headers.
|
||||||
return window
|
return window
|
||||||
@ -453,18 +436,18 @@ function getGlobal() {
|
|||||||
* @Returns true if x is an Array or a TypedArray, false otherwise.
|
* @Returns true if x is an Array or a TypedArray, false otherwise.
|
||||||
*/
|
*/
|
||||||
function isArrayOrTypedArray(x) {
|
function isArrayOrTypedArray(x) {
|
||||||
return Boolean(typeof x === 'object' && (Array.isArray(x) || (ArrayBuffer.isView(x) && !(x instanceof DataView))))
|
return Boolean(typeof x === "object" && (Array.isArray(x) || (ArrayBuffer.isView(x) && !(x instanceof DataView))))
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeQuerablePromise(promise) {
|
function makeQuerablePromise(promise) {
|
||||||
if (typeof promise !== 'object') {
|
if (typeof promise !== "object") {
|
||||||
throw new Error('promise is not an object.')
|
throw new Error("promise is not an object.")
|
||||||
}
|
}
|
||||||
if (!(promise instanceof Promise)) {
|
if (!(promise instanceof Promise)) {
|
||||||
throw new Error('Argument is not a promise.')
|
throw new Error("Argument is not a promise.")
|
||||||
}
|
}
|
||||||
// Don't modify a promise that's been already modified.
|
// Don't modify a promise that's been already modified.
|
||||||
if ('isResolved' in promise || 'isRejected' in promise || 'isPending' in promise) {
|
if ("isResolved" in promise || "isRejected" in promise || "isPending" in promise) {
|
||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
let isPending = true
|
let isPending = true
|
||||||
@ -478,8 +461,8 @@ function makeQuerablePromise(promise) {
|
|||||||
isPending = false
|
isPending = false
|
||||||
resolvedValue = val
|
resolvedValue = val
|
||||||
return val
|
return val
|
||||||
}
|
},
|
||||||
, function(reason) {
|
function(reason) {
|
||||||
rejectReason = reason
|
rejectReason = reason
|
||||||
isRejected = true
|
isRejected = true
|
||||||
isPending = false
|
isPending = false
|
||||||
@ -487,19 +470,19 @@ function makeQuerablePromise(promise) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
Object.defineProperties(qurPro, {
|
Object.defineProperties(qurPro, {
|
||||||
'isResolved': {
|
isResolved: {
|
||||||
get: () => isResolved
|
get: () => isResolved
|
||||||
}
|
},
|
||||||
, 'resolvedValue': {
|
resolvedValue: {
|
||||||
get: () => resolvedValue
|
get: () => resolvedValue
|
||||||
}
|
},
|
||||||
, 'isPending': {
|
isPending: {
|
||||||
get: () => isPending
|
get: () => isPending
|
||||||
}
|
},
|
||||||
, 'isRejected': {
|
isRejected: {
|
||||||
get: () => isRejected
|
get: () => isRejected
|
||||||
}
|
},
|
||||||
, 'rejectReason': {
|
rejectReason: {
|
||||||
get: () => rejectReason
|
get: () => rejectReason
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -508,25 +491,25 @@ function makeQuerablePromise(promise) {
|
|||||||
|
|
||||||
/* inserts custom html to allow prettifying of inputs */
|
/* inserts custom html to allow prettifying of inputs */
|
||||||
function prettifyInputs(root_element) {
|
function prettifyInputs(root_element) {
|
||||||
root_element.querySelectorAll(`input[type="checkbox"]`).forEach(element => {
|
root_element.querySelectorAll(`input[type="checkbox"]`).forEach((element) => {
|
||||||
if (element.style.display === "none") {
|
if (element.style.display === "none") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var parent = element.parentNode;
|
var parent = element.parentNode
|
||||||
if (!parent.classList.contains("input-toggle")) {
|
if (!parent.classList.contains("input-toggle")) {
|
||||||
var wrapper = document.createElement("div");
|
var wrapper = document.createElement("div")
|
||||||
wrapper.classList.add("input-toggle");
|
wrapper.classList.add("input-toggle")
|
||||||
parent.replaceChild(wrapper, element);
|
parent.replaceChild(wrapper, element)
|
||||||
wrapper.appendChild(element);
|
wrapper.appendChild(element)
|
||||||
var label = document.createElement("label");
|
var label = document.createElement("label")
|
||||||
label.htmlFor = element.id;
|
label.htmlFor = element.id
|
||||||
wrapper.appendChild(label);
|
wrapper.appendChild(label)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
class GenericEventSource {
|
class GenericEventSource {
|
||||||
#events = {};
|
#events = {}
|
||||||
#types = []
|
#types = []
|
||||||
constructor(...eventsTypes) {
|
constructor(...eventsTypes) {
|
||||||
if (Array.isArray(eventsTypes) && eventsTypes.length === 1 && Array.isArray(eventsTypes[0])) {
|
if (Array.isArray(eventsTypes) && eventsTypes.length === 1 && Array.isArray(eventsTypes[0])) {
|
||||||
@ -541,7 +524,7 @@ class GenericEventSource {
|
|||||||
*/
|
*/
|
||||||
addEventListener(name, handler) {
|
addEventListener(name, handler) {
|
||||||
if (!this.#types.includes(name)) {
|
if (!this.#types.includes(name)) {
|
||||||
throw new Error('Invalid event name.')
|
throw new Error("Invalid event name.")
|
||||||
}
|
}
|
||||||
if (this.#events.hasOwnProperty(name)) {
|
if (this.#events.hasOwnProperty(name)) {
|
||||||
this.#events[name].push(handler)
|
this.#events[name].push(handler)
|
||||||
@ -574,13 +557,15 @@ class GenericEventSource {
|
|||||||
if (evs.length <= 0) {
|
if (evs.length <= 0) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
return Promise.allSettled(evs.map((callback) => {
|
return Promise.allSettled(
|
||||||
|
evs.map((callback) => {
|
||||||
try {
|
try {
|
||||||
return Promise.resolve(callback.apply(SD, args))
|
return Promise.resolve(callback.apply(SD, args))
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
return Promise.reject(ex)
|
return Promise.reject(ex)
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,33 +583,31 @@ class ServiceContainer {
|
|||||||
}
|
}
|
||||||
register(params) {
|
register(params) {
|
||||||
if (ServiceContainer.isConstructor(params)) {
|
if (ServiceContainer.isConstructor(params)) {
|
||||||
if (typeof params.name !== 'string') {
|
if (typeof params.name !== "string") {
|
||||||
throw new Error('params.name is not a string.')
|
throw new Error("params.name is not a string.")
|
||||||
}
|
}
|
||||||
params = { name: params.name, definition: params }
|
params = { name: params.name, definition: params }
|
||||||
}
|
}
|
||||||
if (typeof params !== 'object') {
|
if (typeof params !== "object") {
|
||||||
throw new Error('params is not an object.')
|
throw new Error("params is not an object.")
|
||||||
}
|
}
|
||||||
[ 'name',
|
;["name", "definition"].forEach((key) => {
|
||||||
'definition',
|
|
||||||
].forEach((key) => {
|
|
||||||
if (!(key in params)) {
|
if (!(key in params)) {
|
||||||
console.error('Invalid service %o registration.', params)
|
console.error("Invalid service %o registration.", params)
|
||||||
throw new Error(`params.${key} is not defined.`)
|
throw new Error(`params.${key} is not defined.`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const opts = { definition: params.definition }
|
const opts = { definition: params.definition }
|
||||||
if ('dependencies' in params) {
|
if ("dependencies" in params) {
|
||||||
if (Array.isArray(params.dependencies)) {
|
if (Array.isArray(params.dependencies)) {
|
||||||
params.dependencies.forEach((dep) => {
|
params.dependencies.forEach((dep) => {
|
||||||
if (typeof dep !== 'string') {
|
if (typeof dep !== "string") {
|
||||||
throw new Error('dependency name is not a string.')
|
throw new Error("dependency name is not a string.")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
opts.dependencies = params.dependencies
|
opts.dependencies = params.dependencies
|
||||||
} else {
|
} else {
|
||||||
throw new Error('params.dependencies is not an array.')
|
throw new Error("params.dependencies is not an array.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (params.singleton) {
|
if (params.singleton) {
|
||||||
@ -671,10 +654,14 @@ class ServiceContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static isClass(definition) {
|
static isClass(definition) {
|
||||||
return typeof definition === 'function' && Boolean(definition.prototype) && definition.prototype.constructor === definition
|
return (
|
||||||
|
typeof definition === "function" &&
|
||||||
|
Boolean(definition.prototype) &&
|
||||||
|
definition.prototype.constructor === definition
|
||||||
|
)
|
||||||
}
|
}
|
||||||
static isConstructor(definition) {
|
static isConstructor(definition) {
|
||||||
return typeof definition === 'function'
|
return typeof definition === "function"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,14 +680,14 @@ function createElement(tagName, attributes, classes, textOrElements) {
|
|||||||
if (value !== undefined && value !== null) {
|
if (value !== undefined && value !== null) {
|
||||||
element.setAttribute(key, value)
|
element.setAttribute(key, value)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
if (classes) {
|
if (classes) {
|
||||||
(Array.isArray(classes) ? classes : [classes]).forEach(className => element.classList.add(className))
|
;(Array.isArray(classes) ? classes : [classes]).forEach((className) => element.classList.add(className))
|
||||||
}
|
}
|
||||||
if (textOrElements) {
|
if (textOrElements) {
|
||||||
const children = Array.isArray(textOrElements) ? textOrElements : [textOrElements]
|
const children = Array.isArray(textOrElements) ? textOrElements : [textOrElements]
|
||||||
children.forEach(textOrElem => {
|
children.forEach((textOrElem) => {
|
||||||
if (textOrElem instanceof Node) {
|
if (textOrElem instanceof Node) {
|
||||||
element.appendChild(textOrElem)
|
element.appendChild(textOrElem)
|
||||||
} else {
|
} else {
|
||||||
@ -752,31 +739,31 @@ Array.prototype.addEventListener = function(method, callback) {
|
|||||||
*/
|
*/
|
||||||
function createTab(request) {
|
function createTab(request) {
|
||||||
if (!request?.id) {
|
if (!request?.id) {
|
||||||
console.error('createTab() error - id is required', Error().stack)
|
console.error("createTab() error - id is required", Error().stack)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.label) {
|
if (!request.label) {
|
||||||
console.error('createTab() error - label is required', Error().stack)
|
console.error("createTab() error - label is required", Error().stack)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.icon) {
|
if (!request.icon) {
|
||||||
console.error('createTab() error - icon is required', Error().stack)
|
console.error("createTab() error - icon is required", Error().stack)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.content && !request.onOpen) {
|
if (!request.content && !request.onOpen) {
|
||||||
console.error('createTab() error - content or onOpen required', Error().stack)
|
console.error("createTab() error - content or onOpen required", Error().stack)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabsContainer = document.querySelector('.tab-container')
|
const tabsContainer = document.querySelector(".tab-container")
|
||||||
if (!tabsContainer) {
|
if (!tabsContainer) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabsContentWrapper = document.querySelector('#tab-content-wrapper')
|
const tabsContentWrapper = document.querySelector("#tab-content-wrapper")
|
||||||
if (!tabsContentWrapper) {
|
if (!tabsContentWrapper) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -784,41 +771,37 @@ Array.prototype.addEventListener = function(method, callback) {
|
|||||||
// console.debug('creating tab: ', request)
|
// console.debug('creating tab: ', request)
|
||||||
|
|
||||||
if (request.css) {
|
if (request.css) {
|
||||||
document.querySelector('body').insertAdjacentElement(
|
document
|
||||||
'beforeend',
|
.querySelector("body")
|
||||||
createElement('style', { id: `tab-${request.id}-css` }, undefined, request.css),
|
.insertAdjacentElement(
|
||||||
|
"beforeend",
|
||||||
|
createElement("style", { id: `tab-${request.id}-css` }, undefined, request.css)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const label = typeof request.label === 'function' ? request.label() : request.label
|
const label = typeof request.label === "function" ? request.label() : request.label
|
||||||
const labelElement = label instanceof Node ? label : createElement('span', undefined, undefined, label)
|
const labelElement = label instanceof Node ? label : createElement("span", undefined, undefined, label)
|
||||||
|
|
||||||
const tab = createElement(
|
const tab = createElement(
|
||||||
'span',
|
"span",
|
||||||
{ id: `tab-${request.id}`, 'data-times-opened': 0 },
|
{ id: `tab-${request.id}`, "data-times-opened": 0 },
|
||||||
['tab'],
|
["tab"],
|
||||||
createElement(
|
createElement("span", undefined, undefined, [
|
||||||
'span',
|
createElement("i", { style: "margin-right: 0.25em" }, [
|
||||||
undefined,
|
"fa-solid",
|
||||||
undefined,
|
`${request.icon.startsWith("fa-") ? "" : "fa-"}${request.icon}`,
|
||||||
[
|
"icon"
|
||||||
createElement(
|
]),
|
||||||
'i',
|
labelElement
|
||||||
{ style: 'margin-right: 0.25em' },
|
])
|
||||||
['fa-solid', `${request.icon.startsWith('fa-') ? '' : 'fa-'}${request.icon}`, 'icon'],
|
|
||||||
),
|
|
||||||
labelElement,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
tabsContainer.insertAdjacentElement("beforeend", tab)
|
||||||
|
|
||||||
tabsContainer.insertAdjacentElement('beforeend', tab)
|
const wrapper = createElement("div", { id: request.id }, ["tab-content-inner"], "Loading..")
|
||||||
|
|
||||||
const wrapper = createElement('div', { id: request.id }, ['tab-content-inner'], 'Loading..')
|
const tabContent = createElement("div", { id: `tab-content-${request.id}` }, ["tab-content"], wrapper)
|
||||||
|
tabsContentWrapper.insertAdjacentElement("beforeend", tabContent)
|
||||||
const tabContent = createElement('div', { id: `tab-content-${request.id}` }, ['tab-content'], wrapper)
|
|
||||||
tabsContentWrapper.insertAdjacentElement('beforeend', tabContent)
|
|
||||||
|
|
||||||
linkTabContents(tab)
|
linkTabContents(tab)
|
||||||
|
|
||||||
@ -826,7 +809,7 @@ Array.prototype.addEventListener = function(method, callback) {
|
|||||||
if (resultFactory === undefined || resultFactory === null) {
|
if (resultFactory === undefined || resultFactory === null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const result = typeof resultFactory === 'function' ? resultFactory() : resultFactory
|
const result = typeof resultFactory === "function" ? resultFactory() : resultFactory
|
||||||
if (result instanceof Promise) {
|
if (result instanceof Promise) {
|
||||||
result.then(replaceContent)
|
result.then(replaceContent)
|
||||||
} else if (result instanceof Node) {
|
} else if (result instanceof Node) {
|
||||||
@ -838,7 +821,7 @@ Array.prototype.addEventListener = function(method, callback) {
|
|||||||
|
|
||||||
replaceContent(request.content)
|
replaceContent(request.content)
|
||||||
|
|
||||||
tab.addEventListener('click', (e) => {
|
tab.addEventListener("click", (e) => {
|
||||||
const timesOpened = +(tab.dataset.timesOpened || 0) + 1
|
const timesOpened = +(tab.dataset.timesOpened || 0) + 1
|
||||||
tab.dataset.timesOpened = timesOpened
|
tab.dataset.timesOpened = timesOpened
|
||||||
|
|
||||||
@ -848,9 +831,9 @@ Array.prototype.addEventListener = function(method, callback) {
|
|||||||
contentElement: wrapper,
|
contentElement: wrapper,
|
||||||
labelElement,
|
labelElement,
|
||||||
timesOpened,
|
timesOpened,
|
||||||
firstOpen: timesOpened === 1,
|
firstOpen: timesOpened === 1
|
||||||
},
|
},
|
||||||
e,
|
e
|
||||||
)
|
)
|
||||||
|
|
||||||
replaceContent(result)
|
replaceContent(result)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
(function () {
|
;(function() {
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
let autoScroll = document.querySelector("#auto_scroll")
|
let autoScroll = document.querySelector("#auto_scroll")
|
||||||
@ -6,23 +6,27 @@
|
|||||||
// observe for changes in the preview pane
|
// observe for changes in the preview pane
|
||||||
var observer = new MutationObserver(function(mutations) {
|
var observer = new MutationObserver(function(mutations) {
|
||||||
mutations.forEach(function(mutation) {
|
mutations.forEach(function(mutation) {
|
||||||
if (mutation.target.className == 'img-batch') {
|
if (mutation.target.className == "img-batch") {
|
||||||
Autoscroll(mutation.target)
|
Autoscroll(mutation.target)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
observer.observe(document.getElementById('preview'), {
|
observer.observe(document.getElementById("preview"), {
|
||||||
childList: true,
|
childList: true,
|
||||||
subtree: true
|
subtree: true
|
||||||
})
|
})
|
||||||
|
|
||||||
function Autoscroll(target) {
|
function Autoscroll(target) {
|
||||||
if (autoScroll.checked && target !== null) {
|
if (autoScroll.checked && target !== null) {
|
||||||
const img = target.querySelector('img')
|
const img = target.querySelector("img")
|
||||||
img.addEventListener('load', function() {
|
img.addEventListener(
|
||||||
img.closest('.imageTaskContainer').scrollIntoView()
|
"load",
|
||||||
}, { once: true })
|
function() {
|
||||||
|
img.closest(".imageTaskContainer").scrollIntoView()
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
(function () { "use strict"
|
;(function() {
|
||||||
if (typeof editorModifierTagsList !== 'object') {
|
"use strict"
|
||||||
console.error('editorModifierTagsList missing...')
|
if (typeof editorModifierTagsList !== "object") {
|
||||||
|
console.error("editorModifierTagsList missing...")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const styleSheet = document.createElement("style");
|
const styleSheet = document.createElement("style")
|
||||||
styleSheet.textContent = `
|
styleSheet.textContent = `
|
||||||
.modifier-card-tiny.drag-sort-active {
|
.modifier-card-tiny.drag-sort-active {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 2px dashed white;
|
border: 2px dashed white;
|
||||||
opacity:0.2;
|
opacity:0.2;
|
||||||
}
|
}
|
||||||
`;
|
`
|
||||||
document.head.appendChild(styleSheet);
|
document.head.appendChild(styleSheet)
|
||||||
|
|
||||||
// observe for changes in tag list
|
// observe for changes in tag list
|
||||||
const observer = new MutationObserver(function(mutations) {
|
const observer = new MutationObserver(function(mutations) {
|
||||||
@ -29,65 +30,87 @@
|
|||||||
|
|
||||||
let current
|
let current
|
||||||
function ModifierDragAndDrop(target) {
|
function ModifierDragAndDrop(target) {
|
||||||
let overlays = document.querySelector('#editor-inputs-tags-list').querySelectorAll('.modifier-card-overlay')
|
let overlays = document.querySelector("#editor-inputs-tags-list").querySelectorAll(".modifier-card-overlay")
|
||||||
overlays.forEach (i => {
|
overlays.forEach((i) => {
|
||||||
i.parentElement.draggable = true;
|
i.parentElement.draggable = true
|
||||||
|
|
||||||
i.parentElement.ondragstart = (e) => {
|
i.parentElement.ondragstart = (e) => {
|
||||||
current = i
|
current = i
|
||||||
i.parentElement.getElementsByClassName('modifier-card-image-overlay')[0].innerText = ''
|
i.parentElement.getElementsByClassName("modifier-card-image-overlay")[0].innerText = ""
|
||||||
i.parentElement.draggable = true
|
i.parentElement.draggable = true
|
||||||
i.parentElement.classList.add('drag-sort-active')
|
i.parentElement.classList.add("drag-sort-active")
|
||||||
for(let item of document.querySelector('#editor-inputs-tags-list').getElementsByClassName('modifier-card-image-overlay')) {
|
for (let item of document
|
||||||
if (item.parentElement.parentElement.getElementsByClassName('modifier-card-overlay')[0] != current) {
|
.querySelector("#editor-inputs-tags-list")
|
||||||
item.parentElement.parentElement.getElementsByClassName('modifier-card-image-overlay')[0].style.opacity = 0
|
.getElementsByClassName("modifier-card-image-overlay")) {
|
||||||
if(item.parentElement.getElementsByClassName('modifier-card-image').length > 0) {
|
if (
|
||||||
item.parentElement.getElementsByClassName('modifier-card-image')[0].style.filter = 'none'
|
item.parentElement.parentElement.getElementsByClassName("modifier-card-overlay")[0] != current
|
||||||
|
) {
|
||||||
|
item.parentElement.parentElement.getElementsByClassName(
|
||||||
|
"modifier-card-image-overlay"
|
||||||
|
)[0].style.opacity = 0
|
||||||
|
if (item.parentElement.getElementsByClassName("modifier-card-image").length > 0) {
|
||||||
|
item.parentElement.getElementsByClassName("modifier-card-image")[0].style.filter = "none"
|
||||||
}
|
}
|
||||||
item.parentElement.parentElement.style.transform = 'none'
|
item.parentElement.parentElement.style.transform = "none"
|
||||||
item.parentElement.parentElement.style.boxShadow = 'none'
|
item.parentElement.parentElement.style.boxShadow = "none"
|
||||||
}
|
}
|
||||||
item.innerText = ''
|
item.innerText = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i.ondragenter = (e) => {
|
i.ondragenter = (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (i != current) {
|
if (i != current) {
|
||||||
let currentPos = 0, droppedPos = 0;
|
let currentPos = 0,
|
||||||
|
droppedPos = 0
|
||||||
for (let it = 0; it < overlays.length; it++) {
|
for (let it = 0; it < overlays.length; it++) {
|
||||||
if (current == overlays[it]) { currentPos = it; }
|
if (current == overlays[it]) {
|
||||||
if (i == overlays[it]) { droppedPos = it; }
|
currentPos = it
|
||||||
|
}
|
||||||
|
if (i == overlays[it]) {
|
||||||
|
droppedPos = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i.parentElement != current.parentElement) {
|
if (i.parentElement != current.parentElement) {
|
||||||
let currentPos = 0, droppedPos = 0
|
let currentPos = 0,
|
||||||
|
droppedPos = 0
|
||||||
for (let it = 0; it < overlays.length; it++) {
|
for (let it = 0; it < overlays.length; it++) {
|
||||||
if (current == overlays[it]) { currentPos = it }
|
if (current == overlays[it]) {
|
||||||
if (i == overlays[it]) { droppedPos = it }
|
currentPos = it
|
||||||
|
}
|
||||||
|
if (i == overlays[it]) {
|
||||||
|
droppedPos = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (currentPos < droppedPos) {
|
if (currentPos < droppedPos) {
|
||||||
current = i.parentElement.parentNode.insertBefore(current.parentElement, i.parentElement.nextSibling).getElementsByClassName('modifier-card-overlay')[0]
|
current = i.parentElement.parentNode
|
||||||
|
.insertBefore(current.parentElement, i.parentElement.nextSibling)
|
||||||
|
.getElementsByClassName("modifier-card-overlay")[0]
|
||||||
} else {
|
} else {
|
||||||
current = i.parentElement.parentNode.insertBefore(current.parentElement, i.parentElement).getElementsByClassName('modifier-card-overlay')[0]
|
current = i.parentElement.parentNode
|
||||||
|
.insertBefore(current.parentElement, i.parentElement)
|
||||||
|
.getElementsByClassName("modifier-card-overlay")[0]
|
||||||
}
|
}
|
||||||
// update activeTags
|
// update activeTags
|
||||||
const tag = activeTags.splice(currentPos, 1)
|
const tag = activeTags.splice(currentPos, 1)
|
||||||
activeTags.splice(droppedPos, 0, tag[0])
|
activeTags.splice(droppedPos, 0, tag[0])
|
||||||
document.dispatchEvent(new Event('refreshImageModifiers'))
|
document.dispatchEvent(new Event("refreshImageModifiers"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
i.ondragover = (e) => {
|
i.ondragover = (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
i.parentElement.ondragend = (e) => {
|
i.parentElement.ondragend = (e) => {
|
||||||
i.parentElement.classList.remove('drag-sort-active')
|
i.parentElement.classList.remove("drag-sort-active")
|
||||||
for(let item of document.querySelector('#editor-inputs-tags-list').getElementsByClassName('modifier-card-image-overlay')) {
|
for (let item of document
|
||||||
item.style.opacity = ''
|
.querySelector("#editor-inputs-tags-list")
|
||||||
item.innerText = '-'
|
.getElementsByClassName("modifier-card-image-overlay")) {
|
||||||
|
item.style.opacity = ""
|
||||||
|
item.innerText = "-"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
(function () {
|
;(function() {
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
const MAX_WEIGHT = 5
|
const MAX_WEIGHT = 5
|
||||||
|
|
||||||
if (typeof editorModifierTagsList !== 'object') {
|
if (typeof editorModifierTagsList !== "object") {
|
||||||
console.error('editorModifierTagsList missing...')
|
console.error("editorModifierTagsList missing...")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,14 +22,16 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
function ModifierMouseWheel(target) {
|
function ModifierMouseWheel(target) {
|
||||||
let overlays = document.querySelector('#editor-inputs-tags-list').querySelectorAll('.modifier-card-overlay')
|
let overlays = document.querySelector("#editor-inputs-tags-list").querySelectorAll(".modifier-card-overlay")
|
||||||
overlays.forEach (i => {
|
overlays.forEach((i) => {
|
||||||
i.onwheel = (e) => {
|
i.onwheel = (e) => {
|
||||||
if (e.ctrlKey == true) {
|
if (e.ctrlKey == true) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
const delta = Math.sign(event.deltaY)
|
const delta = Math.sign(event.deltaY)
|
||||||
let s = i.parentElement.getElementsByClassName('modifier-card-label')[0].getElementsByTagName("p")[0].innerText
|
let s = i.parentElement
|
||||||
|
.getElementsByClassName("modifier-card-label")[0]
|
||||||
|
.getElementsByTagName("p")[0].innerText
|
||||||
let t
|
let t
|
||||||
// find the corresponding tag
|
// find the corresponding tag
|
||||||
for (let it = 0; it < overlays.length; it++) {
|
for (let it = 0; it < overlays.length; it++) {
|
||||||
@ -38,43 +40,40 @@
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s.charAt(0) !== '(' && s.charAt(s.length - 1) !== ')' && s.trim().includes(' ')) {
|
if (s.charAt(0) !== "(" && s.charAt(s.length - 1) !== ")" && s.trim().includes(" ")) {
|
||||||
s = '(' + s + ')'
|
s = "(" + s + ")"
|
||||||
t = '(' + t + ')'
|
t = "(" + t + ")"
|
||||||
}
|
}
|
||||||
if (delta < 0) {
|
if (delta < 0) {
|
||||||
// wheel scrolling up
|
// wheel scrolling up
|
||||||
if (s.substring(s.length - 1) == '-') {
|
if (s.substring(s.length - 1) == "-") {
|
||||||
s = s.substring(0, s.length - 1)
|
s = s.substring(0, s.length - 1)
|
||||||
t = t.substring(0, t.length - 1)
|
t = t.substring(0, t.length - 1)
|
||||||
}
|
} else {
|
||||||
else
|
if (s.substring(s.length - MAX_WEIGHT) !== "+".repeat(MAX_WEIGHT)) {
|
||||||
{
|
s = s + "+"
|
||||||
if (s.substring(s.length - MAX_WEIGHT) !== '+'.repeat(MAX_WEIGHT)) {
|
t = t + "+"
|
||||||
s = s + '+'
|
|
||||||
t = t + '+'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
// wheel scrolling down
|
// wheel scrolling down
|
||||||
if (s.substring(s.length - 1) == '+') {
|
if (s.substring(s.length - 1) == "+") {
|
||||||
s = s.substring(0, s.length - 1)
|
s = s.substring(0, s.length - 1)
|
||||||
t = t.substring(0, t.length - 1)
|
t = t.substring(0, t.length - 1)
|
||||||
}
|
} else {
|
||||||
else
|
if (s.substring(s.length - MAX_WEIGHT) !== "-".repeat(MAX_WEIGHT)) {
|
||||||
{
|
s = s + "-"
|
||||||
if (s.substring(s.length - MAX_WEIGHT) !== '-'.repeat(MAX_WEIGHT)) {
|
t = t + "-"
|
||||||
s = s + '-'
|
|
||||||
t = t + '-'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s.charAt(0) === '(' && s.charAt(s.length - 1) === ')') {
|
if (s.charAt(0) === "(" && s.charAt(s.length - 1) === ")") {
|
||||||
s = s.substring(1, s.length - 1)
|
s = s.substring(1, s.length - 1)
|
||||||
t = t.substring(1, t.length - 1)
|
t = t.substring(1, t.length - 1)
|
||||||
}
|
}
|
||||||
i.parentElement.getElementsByClassName('modifier-card-label')[0].getElementsByTagName("p")[0].innerText = s
|
i.parentElement
|
||||||
|
.getElementsByClassName("modifier-card-label")[0]
|
||||||
|
.getElementsByTagName("p")[0].innerText = s
|
||||||
// update activeTags
|
// update activeTags
|
||||||
for (let it = 0; it < overlays.length; it++) {
|
for (let it = 0; it < overlays.length; it++) {
|
||||||
if (i == overlays[it]) {
|
if (i == overlays[it]) {
|
||||||
@ -82,7 +81,7 @@
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.dispatchEvent(new Event('refreshImageModifiers'))
|
document.dispatchEvent(new Event("refreshImageModifiers"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
(function() {
|
;(function() {
|
||||||
PLUGINS['MODIFIERS_LOAD'].push({
|
PLUGINS["MODIFIERS_LOAD"].push({
|
||||||
loader: function() {
|
loader: function() {
|
||||||
let customModifiers = localStorage.getItem(CUSTOM_MODIFIERS_KEY, '')
|
let customModifiers = localStorage.getItem(CUSTOM_MODIFIERS_KEY, "")
|
||||||
customModifiersTextBox.value = customModifiers
|
customModifiersTextBox.value = customModifiers
|
||||||
|
|
||||||
if (customModifiersGroupElement !== undefined) {
|
if (customModifiersGroupElement !== undefined) {
|
||||||
customModifiersGroupElement.remove()
|
customModifiersGroupElement.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customModifiers && customModifiers.trim() !== '') {
|
if (customModifiers && customModifiers.trim() !== "") {
|
||||||
customModifiers = customModifiers.split('\n')
|
customModifiers = customModifiers.split("\n")
|
||||||
customModifiers = customModifiers.filter(m => m.trim() !== '')
|
customModifiers = customModifiers.filter((m) => m.trim() !== "")
|
||||||
customModifiers = customModifiers.map(function(m) {
|
customModifiers = customModifiers.map(function(m) {
|
||||||
return {
|
return {
|
||||||
"modifier": m
|
modifier: m
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let customGroup = {
|
let customGroup = {
|
||||||
'category': 'Custom Modifiers',
|
category: "Custom Modifiers",
|
||||||
'modifiers': customModifiers
|
modifiers: customModifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
customModifiersGroupElement = createModifierGroup(customGroup, true)
|
customModifiersGroupElement = createModifierGroup(customGroup, true)
|
||||||
|
@ -26,8 +26,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
after `jasmine.js` and `jasmine_html.js`, but before `boot1.js` or any project
|
after `jasmine.js` and `jasmine_html.js`, but before `boot1.js` or any project
|
||||||
source files or spec files are loaded.
|
source files or spec files are loaded.
|
||||||
*/
|
*/
|
||||||
(function() {
|
;(function() {
|
||||||
const jasmineRequire = window.jasmineRequire || require('./jasmine.js');
|
const jasmineRequire = window.jasmineRequire || require("./jasmine.js")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ## Require & Instantiate
|
* ## Require & Instantiate
|
||||||
@ -35,30 +35,30 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
|
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
|
||||||
*/
|
*/
|
||||||
const jasmine = jasmineRequire.core(jasmineRequire),
|
const jasmine = jasmineRequire.core(jasmineRequire),
|
||||||
global = jasmine.getGlobal();
|
global = jasmine.getGlobal()
|
||||||
global.jasmine = jasmine;
|
global.jasmine = jasmine
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
|
* Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
|
||||||
*/
|
*/
|
||||||
jasmineRequire.html(jasmine);
|
jasmineRequire.html(jasmine)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the Jasmine environment. This is used to run all specs in a project.
|
* Create the Jasmine environment. This is used to run all specs in a project.
|
||||||
*/
|
*/
|
||||||
const env = jasmine.getEnv();
|
const env = jasmine.getEnv()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ## The Global Interface
|
* ## The Global Interface
|
||||||
*
|
*
|
||||||
* Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
|
* Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
|
||||||
*/
|
*/
|
||||||
const jasmineInterface = jasmineRequire.interface(jasmine, env);
|
const jasmineInterface = jasmineRequire.interface(jasmine, env)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
|
* Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
|
||||||
*/
|
*/
|
||||||
for (const property in jasmineInterface) {
|
for (const property in jasmineInterface) {
|
||||||
global[property] = jasmineInterface[property];
|
global[property] = jasmineInterface[property]
|
||||||
}
|
}
|
||||||
})();
|
})()
|
||||||
|
@ -33,8 +33,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
after `boot0.js` is loaded and before this file is loaded.
|
after `boot0.js` is loaded and before this file is loaded.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
;(function() {
|
||||||
const env = jasmine.getEnv();
|
const env = jasmine.getEnv()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ## Runner Parameters
|
* ## Runner Parameters
|
||||||
@ -44,29 +44,27 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
|
|
||||||
const queryString = new jasmine.QueryString({
|
const queryString = new jasmine.QueryString({
|
||||||
getWindowLocation: function() {
|
getWindowLocation: function() {
|
||||||
return window.location;
|
return window.location
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const filterSpecs = !!queryString.getParam('spec');
|
const filterSpecs = !!queryString.getParam("spec")
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
stopOnSpecFailure: queryString.getParam('stopOnSpecFailure'),
|
stopOnSpecFailure: queryString.getParam("stopOnSpecFailure"),
|
||||||
stopSpecOnExpectationFailure: queryString.getParam(
|
stopSpecOnExpectationFailure: queryString.getParam("stopSpecOnExpectationFailure"),
|
||||||
'stopSpecOnExpectationFailure'
|
hideDisabled: queryString.getParam("hideDisabled")
|
||||||
),
|
|
||||||
hideDisabled: queryString.getParam('hideDisabled')
|
|
||||||
};
|
|
||||||
|
|
||||||
const random = queryString.getParam('random');
|
|
||||||
|
|
||||||
if (random !== undefined && random !== '') {
|
|
||||||
config.random = random;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const seed = queryString.getParam('seed');
|
const random = queryString.getParam("random")
|
||||||
|
|
||||||
|
if (random !== undefined && random !== "") {
|
||||||
|
config.random = random
|
||||||
|
}
|
||||||
|
|
||||||
|
const seed = queryString.getParam("seed")
|
||||||
if (seed) {
|
if (seed) {
|
||||||
config.seed = seed;
|
config.seed = seed
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,57 +74,57 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
const htmlReporter = new jasmine.HtmlReporter({
|
const htmlReporter = new jasmine.HtmlReporter({
|
||||||
env: env,
|
env: env,
|
||||||
navigateWithNewParam: function(key, value) {
|
navigateWithNewParam: function(key, value) {
|
||||||
return queryString.navigateWithNewParam(key, value);
|
return queryString.navigateWithNewParam(key, value)
|
||||||
},
|
},
|
||||||
addToExistingQueryString: function(key, value) {
|
addToExistingQueryString: function(key, value) {
|
||||||
return queryString.fullStringWithNewParam(key, value);
|
return queryString.fullStringWithNewParam(key, value)
|
||||||
},
|
},
|
||||||
getContainer: function() {
|
getContainer: function() {
|
||||||
return document.body;
|
return document.body
|
||||||
},
|
},
|
||||||
createElement: function() {
|
createElement: function() {
|
||||||
return document.createElement.apply(document, arguments);
|
return document.createElement.apply(document, arguments)
|
||||||
},
|
},
|
||||||
createTextNode: function() {
|
createTextNode: function() {
|
||||||
return document.createTextNode.apply(document, arguments);
|
return document.createTextNode.apply(document, arguments)
|
||||||
},
|
},
|
||||||
timer: new jasmine.Timer(),
|
timer: new jasmine.Timer(),
|
||||||
filterSpecs: filterSpecs
|
filterSpecs: filterSpecs
|
||||||
});
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
|
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
|
||||||
*/
|
*/
|
||||||
env.addReporter(jsApiReporter);
|
env.addReporter(jsApiReporter)
|
||||||
env.addReporter(htmlReporter);
|
env.addReporter(htmlReporter)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
|
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
|
||||||
*/
|
*/
|
||||||
const specFilter = new jasmine.HtmlSpecFilter({
|
const specFilter = new jasmine.HtmlSpecFilter({
|
||||||
filterString: function() {
|
filterString: function() {
|
||||||
return queryString.getParam('spec');
|
return queryString.getParam("spec")
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
config.specFilter = function(spec) {
|
config.specFilter = function(spec) {
|
||||||
return specFilter.matches(spec.getFullName());
|
return specFilter.matches(spec.getFullName())
|
||||||
};
|
}
|
||||||
|
|
||||||
env.configure(config);
|
env.configure(config)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ## Execution
|
* ## Execution
|
||||||
*
|
*
|
||||||
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
|
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
|
||||||
*/
|
*/
|
||||||
const currentWindowOnload = window.onload;
|
const currentWindowOnload = window.onload
|
||||||
|
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
if (currentWindowOnload) {
|
if (currentWindowOnload) {
|
||||||
currentWindowOnload();
|
currentWindowOnload()
|
||||||
}
|
}
|
||||||
htmlReporter.initialize();
|
htmlReporter.initialize()
|
||||||
env.execute();
|
env.execute()
|
||||||
};
|
}
|
||||||
})();
|
})()
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -16,20 +16,20 @@ beforeEach(function () {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('stable-diffusion-ui', function() {
|
describe("stable-diffusion-ui", function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
expect(typeof SD).toBe('object')
|
expect(typeof SD).toBe("object")
|
||||||
expect(typeof SD.serverState).toBe('object')
|
expect(typeof SD.serverState).toBe("object")
|
||||||
expect(typeof SD.serverState.status).toBe('string')
|
expect(typeof SD.serverState.status).toBe("string")
|
||||||
})
|
})
|
||||||
it('should be able to reach the backend', async function() {
|
it("should be able to reach the backend", async function() {
|
||||||
expect(SD.serverState.status).toBe(SD.ServerStates.unavailable)
|
expect(SD.serverState.status).toBe(SD.ServerStates.unavailable)
|
||||||
SD.sessionId = JASMINE_SESSION_ID
|
SD.sessionId = JASMINE_SESSION_ID
|
||||||
await SD.init()
|
await SD.init()
|
||||||
expect(SD.isServerAvailable()).toBeTrue()
|
expect(SD.isServerAvailable()).toBeTrue()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('enfore the current task state', function() {
|
it("enfore the current task state", function() {
|
||||||
const task = new SD.Task()
|
const task = new SD.Task()
|
||||||
expect(task.status).toBe(SD.TaskStatus.init)
|
expect(task.status).toBe(SD.TaskStatus.init)
|
||||||
expect(task.isPending).toBeTrue()
|
expect(task.isPending).toBeTrue()
|
||||||
@ -65,149 +65,161 @@ describe('stable-diffusion-ui', function() {
|
|||||||
task._setStatus(SD.TaskStatus.completed)
|
task._setStatus(SD.TaskStatus.completed)
|
||||||
}).toThrowError()
|
}).toThrowError()
|
||||||
})
|
})
|
||||||
it('should be able to run tasks', async function() {
|
it("should be able to run tasks", async function() {
|
||||||
expect(typeof SD.Task.run).toBe('function')
|
expect(typeof SD.Task.run).toBe("function")
|
||||||
const promiseGenerator = (function*(val) {
|
const promiseGenerator = (function*(val) {
|
||||||
expect(val).toBe('start')
|
expect(val).toBe("start")
|
||||||
expect(yield 1 + 1).toBe(4)
|
expect(yield 1 + 1).toBe(4)
|
||||||
expect(yield 2 + 2).toBe(8)
|
expect(yield 2 + 2).toBe(8)
|
||||||
yield asyncDelay(500)
|
yield asyncDelay(500)
|
||||||
expect(yield 3 + 3).toBe(12)
|
expect(yield 3 + 3).toBe(12)
|
||||||
expect(yield 4 + 4).toBe(16)
|
expect(yield 4 + 4).toBe(16)
|
||||||
return 8 + 8
|
return 8 + 8
|
||||||
})('start')
|
})("start")
|
||||||
const callback = function({ value, done }) {
|
const callback = function({ value, done }) {
|
||||||
return { value: 2 * value, done }
|
return { value: 2 * value, done }
|
||||||
}
|
}
|
||||||
expect(await SD.Task.run(promiseGenerator, { callback })).toBe(32)
|
expect(await SD.Task.run(promiseGenerator, { callback })).toBe(32)
|
||||||
})
|
})
|
||||||
it('should be able to queue tasks', async function() {
|
it("should be able to queue tasks", async function() {
|
||||||
expect(typeof SD.Task.enqueue).toBe('function')
|
expect(typeof SD.Task.enqueue).toBe("function")
|
||||||
const promiseGenerator = (function*(val) {
|
const promiseGenerator = (function*(val) {
|
||||||
expect(val).toBe('start')
|
expect(val).toBe("start")
|
||||||
expect(yield 1 + 1).toBe(4)
|
expect(yield 1 + 1).toBe(4)
|
||||||
expect(yield 2 + 2).toBe(8)
|
expect(yield 2 + 2).toBe(8)
|
||||||
yield asyncDelay(500)
|
yield asyncDelay(500)
|
||||||
expect(yield 3 + 3).toBe(12)
|
expect(yield 3 + 3).toBe(12)
|
||||||
expect(yield 4 + 4).toBe(16)
|
expect(yield 4 + 4).toBe(16)
|
||||||
return 8 + 8
|
return 8 + 8
|
||||||
})('start')
|
})("start")
|
||||||
const callback = function({ value, done }) {
|
const callback = function({ value, done }) {
|
||||||
return { value: 2 * value, done }
|
return { value: 2 * value, done }
|
||||||
}
|
}
|
||||||
const gen = SD.Task.asGenerator({ generator: promiseGenerator, callback })
|
const gen = SD.Task.asGenerator({ generator: promiseGenerator, callback })
|
||||||
expect(await SD.Task.enqueue(gen)).toBe(32)
|
expect(await SD.Task.enqueue(gen)).toBe(32)
|
||||||
})
|
})
|
||||||
it('should be able to chain handlers', async function() {
|
it("should be able to chain handlers", async function() {
|
||||||
expect(typeof SD.Task.enqueue).toBe('function')
|
expect(typeof SD.Task.enqueue).toBe("function")
|
||||||
const promiseGenerator = (function*(val) {
|
const promiseGenerator = (function*(val) {
|
||||||
expect(val).toBe('start')
|
expect(val).toBe("start")
|
||||||
expect(yield {test: '1'}).toEqual({test: '1', foo: 'bar'})
|
expect(yield { test: "1" }).toEqual({ test: "1", foo: "bar" })
|
||||||
expect(yield 2 + 2).toEqual(8)
|
expect(yield 2 + 2).toEqual(8)
|
||||||
yield asyncDelay(500)
|
yield asyncDelay(500)
|
||||||
expect(yield 3 + 3).toEqual(12)
|
expect(yield 3 + 3).toEqual(12)
|
||||||
expect(yield {test: 4}).toEqual({test: 8, foo: 'bar'})
|
expect(yield { test: 4 }).toEqual({ test: 8, foo: "bar" })
|
||||||
return { test: 8 }
|
return { test: 8 }
|
||||||
})('start')
|
})("start")
|
||||||
const gen1 = SD.Task.asGenerator({generator: promiseGenerator, callback: function({value, done}) {
|
const gen1 = SD.Task.asGenerator({
|
||||||
|
generator: promiseGenerator,
|
||||||
|
callback: function({ value, done }) {
|
||||||
if (typeof value === "object") {
|
if (typeof value === "object") {
|
||||||
value['foo'] = 'bar'
|
value["foo"] = "bar"
|
||||||
}
|
}
|
||||||
return { value, done }
|
return { value, done }
|
||||||
}})
|
}
|
||||||
const gen2 = SD.Task.asGenerator({generator: gen1, callback: function({value, done}) {
|
})
|
||||||
if (typeof value === 'number') {
|
const gen2 = SD.Task.asGenerator({
|
||||||
|
generator: gen1,
|
||||||
|
callback: function({ value, done }) {
|
||||||
|
if (typeof value === "number") {
|
||||||
value = 2 * value
|
value = 2 * value
|
||||||
}
|
}
|
||||||
if (typeof value === 'object' && typeof value.test === 'number') {
|
if (typeof value === "object" && typeof value.test === "number") {
|
||||||
value.test = 2 * value.test
|
value.test = 2 * value.test
|
||||||
}
|
}
|
||||||
return { value, done }
|
return { value, done }
|
||||||
}})
|
}
|
||||||
expect(await SD.Task.enqueue(gen2)).toEqual({test:32, foo: 'bar'})
|
|
||||||
})
|
})
|
||||||
describe('ServiceContainer', function() {
|
expect(await SD.Task.enqueue(gen2)).toEqual({ test: 32, foo: "bar" })
|
||||||
it('should be able to register providers', function() {
|
})
|
||||||
|
describe("ServiceContainer", function() {
|
||||||
|
it("should be able to register providers", function() {
|
||||||
const cont = new ServiceContainer(
|
const cont = new ServiceContainer(
|
||||||
function foo() {
|
function foo() {
|
||||||
this.bar = ''
|
this.bar = ""
|
||||||
},
|
},
|
||||||
function bar() {
|
function bar() {
|
||||||
return () => 0
|
return () => 0
|
||||||
},
|
},
|
||||||
{ name: 'zero', definition: 0 },
|
{ name: "zero", definition: 0 },
|
||||||
{ name: 'ctx', definition: () => Object.create(null), singleton: true },
|
{ name: "ctx", definition: () => Object.create(null), singleton: true },
|
||||||
{ name: 'test',
|
{
|
||||||
|
name: "test",
|
||||||
definition: (ctx, missing, one, foo) => {
|
definition: (ctx, missing, one, foo) => {
|
||||||
expect(ctx).toEqual({ ran: true })
|
expect(ctx).toEqual({ ran: true })
|
||||||
expect(one).toBe(1)
|
expect(one).toBe(1)
|
||||||
expect(typeof foo).toBe('object')
|
expect(typeof foo).toBe("object")
|
||||||
expect(foo.bar).toBeDefined()
|
expect(foo.bar).toBeDefined()
|
||||||
expect(typeof missing).toBe('undefined')
|
expect(typeof missing).toBe("undefined")
|
||||||
return {foo: 'bar'}
|
return { foo: "bar" }
|
||||||
}, dependencies: ['ctx', 'missing', 'one', 'foo']
|
},
|
||||||
|
dependencies: ["ctx", "missing", "one", "foo"]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const fooObj = cont.get('foo')
|
const fooObj = cont.get("foo")
|
||||||
expect(typeof fooObj).toBe('object')
|
expect(typeof fooObj).toBe("object")
|
||||||
fooObj.ran = true
|
fooObj.ran = true
|
||||||
|
|
||||||
const ctx = cont.get('ctx')
|
const ctx = cont.get("ctx")
|
||||||
expect(ctx).toEqual({})
|
expect(ctx).toEqual({})
|
||||||
ctx.ran = true
|
ctx.ran = true
|
||||||
|
|
||||||
const bar = cont.get('bar')
|
const bar = cont.get("bar")
|
||||||
expect(typeof bar).toBe('function')
|
expect(typeof bar).toBe("function")
|
||||||
expect(bar()).toBe(0)
|
expect(bar()).toBe(0)
|
||||||
|
|
||||||
cont.register({name: 'one', definition: 1})
|
cont.register({ name: "one", definition: 1 })
|
||||||
const test = cont.get('test')
|
const test = cont.get("test")
|
||||||
expect(typeof test).toBe('object')
|
expect(typeof test).toBe("object")
|
||||||
expect(test.foo).toBe('bar')
|
expect(test.foo).toBe("bar")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should be able to stream data in chunks', async function() {
|
it("should be able to stream data in chunks", async function() {
|
||||||
expect(SD.isServerAvailable()).toBeTrue()
|
expect(SD.isServerAvailable()).toBeTrue()
|
||||||
const nbr_steps = 15
|
const nbr_steps = 15
|
||||||
let res = await fetch('/render', {
|
let res = await fetch("/render", {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
"prompt": "a photograph of an astronaut riding a horse",
|
prompt: "a photograph of an astronaut riding a horse",
|
||||||
"negative_prompt": "",
|
negative_prompt: "",
|
||||||
"width": 128,
|
width: 128,
|
||||||
"height": 128,
|
height: 128,
|
||||||
"seed": Math.floor(Math.random() * 10000000),
|
seed: Math.floor(Math.random() * 10000000),
|
||||||
|
|
||||||
"sampler": "plms",
|
sampler: "plms",
|
||||||
"use_stable_diffusion_model": "sd-v1-4",
|
use_stable_diffusion_model: "sd-v1-4",
|
||||||
"num_inference_steps": nbr_steps,
|
num_inference_steps: nbr_steps,
|
||||||
"guidance_scale": 7.5,
|
guidance_scale: 7.5,
|
||||||
|
|
||||||
"numOutputsParallel": 1,
|
numOutputsParallel: 1,
|
||||||
"stream_image_progress": true,
|
stream_image_progress: true,
|
||||||
"show_only_filtered_image": true,
|
show_only_filtered_image: true,
|
||||||
"output_format": "jpeg",
|
output_format: "jpeg",
|
||||||
|
|
||||||
"session_id": JASMINE_SESSION_ID,
|
session_id: JASMINE_SESSION_ID
|
||||||
}),
|
})
|
||||||
})
|
})
|
||||||
expect(res.ok).toBeTruthy()
|
expect(res.ok).toBeTruthy()
|
||||||
const renderRequest = await res.json()
|
const renderRequest = await res.json()
|
||||||
expect(typeof renderRequest.stream).toBe('string')
|
expect(typeof renderRequest.stream).toBe("string")
|
||||||
expect(renderRequest.task).toBeDefined()
|
expect(renderRequest.task).toBeDefined()
|
||||||
|
|
||||||
// Wait for server status to update.
|
// Wait for server status to update.
|
||||||
await SD.waitUntil(() => {
|
await SD.waitUntil(
|
||||||
console.log('Waiting for %s to be received...', renderRequest.task)
|
() => {
|
||||||
return (!SD.serverState.tasks || SD.serverState.tasks[String(renderRequest.task)])
|
console.log("Waiting for %s to be received...", renderRequest.task)
|
||||||
}, 250, 10 * 60 * 1000)
|
return !SD.serverState.tasks || SD.serverState.tasks[String(renderRequest.task)]
|
||||||
|
},
|
||||||
|
250,
|
||||||
|
10 * 60 * 1000
|
||||||
|
)
|
||||||
// Wait for task to start on server.
|
// Wait for task to start on server.
|
||||||
await SD.waitUntil(() => {
|
await SD.waitUntil(() => {
|
||||||
console.log('Waiting for %s to start...', renderRequest.task)
|
console.log("Waiting for %s to start...", renderRequest.task)
|
||||||
return !SD.serverState.tasks || SD.serverState.tasks[String(renderRequest.task)] !== 'pending'
|
return !SD.serverState.tasks || SD.serverState.tasks[String(renderRequest.task)] !== "pending"
|
||||||
}, 250)
|
}, 250)
|
||||||
|
|
||||||
const reader = new SD.ChunkedStreamReader(renderRequest.stream)
|
const reader = new SD.ChunkedStreamReader(renderRequest.stream)
|
||||||
@ -217,11 +229,11 @@ describe('stable-diffusion-ui', function() {
|
|||||||
if (!value || value.length <= 0) {
|
if (!value || value.length <= 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return reader.readStreamAsJSON(value.join(''))
|
return reader.readStreamAsJSON(value.join(""))
|
||||||
}
|
}
|
||||||
reader.onNext = function({ done, value }) {
|
reader.onNext = function({ done, value }) {
|
||||||
console.log(value)
|
console.log(value)
|
||||||
if (typeof value === 'object' && 'status' in value) {
|
if (typeof value === "object" && "status" in value) {
|
||||||
done = true
|
done = true
|
||||||
}
|
}
|
||||||
return { done, value }
|
return { done, value }
|
||||||
@ -231,10 +243,10 @@ describe('stable-diffusion-ui', function() {
|
|||||||
let complete = false
|
let complete = false
|
||||||
//for await (const stepUpdate of reader) {
|
//for await (const stepUpdate of reader) {
|
||||||
for await (const stepUpdate of reader.open()) {
|
for await (const stepUpdate of reader.open()) {
|
||||||
console.log('ChunkedStreamReader received ', stepUpdate)
|
console.log("ChunkedStreamReader received ", stepUpdate)
|
||||||
lastUpdate = stepUpdate
|
lastUpdate = stepUpdate
|
||||||
if (complete) {
|
if (complete) {
|
||||||
expect(stepUpdate.status).toBe('succeeded')
|
expect(stepUpdate.status).toBe("succeeded")
|
||||||
expect(stepUpdate.output).toHaveSize(1)
|
expect(stepUpdate.output).toHaveSize(1)
|
||||||
} else {
|
} else {
|
||||||
expect(stepUpdate.total_steps).toBe(nbr_steps)
|
expect(stepUpdate.total_steps).toBe(nbr_steps)
|
||||||
@ -250,33 +262,35 @@ describe('stable-diffusion-ui', function() {
|
|||||||
res = await fetch(renderRequest.stream)
|
res = await fetch(renderRequest.stream)
|
||||||
expect(res.ok).toBeTruthy()
|
expect(res.ok).toBeTruthy()
|
||||||
const cachedResponse = await res.json()
|
const cachedResponse = await res.json()
|
||||||
console.log('Cache test %s received %o', i, cachedResponse)
|
console.log("Cache test %s received %o", i, cachedResponse)
|
||||||
expect(lastUpdate).toEqual(cachedResponse)
|
expect(lastUpdate).toEqual(cachedResponse)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('should be able to make renders', function() {
|
describe("should be able to make renders", function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
expect(SD.isServerAvailable()).toBeTrue()
|
expect(SD.isServerAvailable()).toBeTrue()
|
||||||
})
|
})
|
||||||
it('basic inline request', async function() {
|
it("basic inline request", async function() {
|
||||||
let stepCount = 0
|
let stepCount = 0
|
||||||
let complete = false
|
let complete = false
|
||||||
const result = await SD.render({
|
const result = await SD.render(
|
||||||
"prompt": "a photograph of an astronaut riding a horse",
|
{
|
||||||
"width": 128,
|
prompt: "a photograph of an astronaut riding a horse",
|
||||||
"height": 128,
|
width: 128,
|
||||||
"num_inference_steps": 10,
|
height: 128,
|
||||||
"show_only_filtered_image": false,
|
num_inference_steps: 10,
|
||||||
|
show_only_filtered_image: false,
|
||||||
//"use_face_correction": 'GFPGANv1.3',
|
//"use_face_correction": 'GFPGANv1.3',
|
||||||
"use_upscale": "RealESRGAN_x4plus",
|
use_upscale: "RealESRGAN_x4plus",
|
||||||
"session_id": JASMINE_SESSION_ID,
|
session_id: JASMINE_SESSION_ID
|
||||||
}, function(event) {
|
},
|
||||||
|
function(event) {
|
||||||
console.log(this, event)
|
console.log(this, event)
|
||||||
if ('update' in event) {
|
if ("update" in event) {
|
||||||
const stepUpdate = event.update
|
const stepUpdate = event.update
|
||||||
if (complete || (stepUpdate.status && stepUpdate.step === stepUpdate.total_steps)) {
|
if (complete || (stepUpdate.status && stepUpdate.step === stepUpdate.total_steps)) {
|
||||||
expect(stepUpdate.status).toBe('succeeded')
|
expect(stepUpdate.status).toBe("succeeded")
|
||||||
expect(stepUpdate.output).toHaveSize(2)
|
expect(stepUpdate.output).toHaveSize(2)
|
||||||
} else {
|
} else {
|
||||||
expect(stepUpdate.step).toBe(stepCount)
|
expect(stepUpdate.step).toBe(stepCount)
|
||||||
@ -287,29 +301,33 @@ describe('stable-diffusion-ui', function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
console.log(result)
|
console.log(result)
|
||||||
expect(result.status).toBe('succeeded')
|
expect(result.status).toBe("succeeded")
|
||||||
expect(result.output).toHaveSize(2)
|
expect(result.output).toHaveSize(2)
|
||||||
})
|
})
|
||||||
it('post and reader request', async function() {
|
it("post and reader request", async function() {
|
||||||
const renderTask = new SD.RenderTask({
|
const renderTask = new SD.RenderTask({
|
||||||
"prompt": "a photograph of an astronaut riding a horse",
|
prompt: "a photograph of an astronaut riding a horse",
|
||||||
"width": 128,
|
width: 128,
|
||||||
"height": 128,
|
height: 128,
|
||||||
"seed": SD.MAX_SEED_VALUE,
|
seed: SD.MAX_SEED_VALUE,
|
||||||
"num_inference_steps": 10,
|
num_inference_steps: 10,
|
||||||
"session_id": JASMINE_SESSION_ID,
|
session_id: JASMINE_SESSION_ID
|
||||||
})
|
})
|
||||||
expect(renderTask.status).toBe(SD.TaskStatus.init)
|
expect(renderTask.status).toBe(SD.TaskStatus.init)
|
||||||
|
|
||||||
const timeout = -1
|
const timeout = -1
|
||||||
const renderRequest = await renderTask.post(timeout)
|
const renderRequest = await renderTask.post(timeout)
|
||||||
expect(typeof renderRequest.stream).toBe('string')
|
expect(typeof renderRequest.stream).toBe("string")
|
||||||
expect(renderTask.status).toBe(SD.TaskStatus.waiting)
|
expect(renderTask.status).toBe(SD.TaskStatus.waiting)
|
||||||
expect(renderTask.streamUrl).toBe(renderRequest.stream)
|
expect(renderTask.streamUrl).toBe(renderRequest.stream)
|
||||||
|
|
||||||
await renderTask.waitUntil({state: SD.TaskStatus.processing, callback: () => console.log('Waiting for render task to start...') })
|
await renderTask.waitUntil({
|
||||||
|
state: SD.TaskStatus.processing,
|
||||||
|
callback: () => console.log("Waiting for render task to start...")
|
||||||
|
})
|
||||||
expect(renderTask.status).toBe(SD.TaskStatus.processing)
|
expect(renderTask.status).toBe(SD.TaskStatus.processing)
|
||||||
|
|
||||||
let stepCount = 0
|
let stepCount = 0
|
||||||
@ -318,7 +336,7 @@ describe('stable-diffusion-ui', function() {
|
|||||||
for await (const stepUpdate of renderTask.reader.open()) {
|
for await (const stepUpdate of renderTask.reader.open()) {
|
||||||
console.log(stepUpdate)
|
console.log(stepUpdate)
|
||||||
if (complete || (stepUpdate.status && stepUpdate.step === stepUpdate.total_steps)) {
|
if (complete || (stepUpdate.status && stepUpdate.step === stepUpdate.total_steps)) {
|
||||||
expect(stepUpdate.status).toBe('succeeded')
|
expect(stepUpdate.status).toBe("succeeded")
|
||||||
expect(stepUpdate.output).toHaveSize(1)
|
expect(stepUpdate.output).toHaveSize(1)
|
||||||
} else {
|
} else {
|
||||||
expect(stepUpdate.step).toBe(stepCount)
|
expect(stepUpdate.step).toBe(stepCount)
|
||||||
@ -330,28 +348,28 @@ describe('stable-diffusion-ui', function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect(renderTask.status).toBe(SD.TaskStatus.completed)
|
expect(renderTask.status).toBe(SD.TaskStatus.completed)
|
||||||
expect(renderTask.result.status).toBe('succeeded')
|
expect(renderTask.result.status).toBe("succeeded")
|
||||||
expect(renderTask.result.output).toHaveSize(1)
|
expect(renderTask.result.output).toHaveSize(1)
|
||||||
})
|
})
|
||||||
it('queued request', async function() {
|
it("queued request", async function() {
|
||||||
let stepCount = 0
|
let stepCount = 0
|
||||||
let complete = false
|
let complete = false
|
||||||
const renderTask = new SD.RenderTask({
|
const renderTask = new SD.RenderTask({
|
||||||
"prompt": "a photograph of an astronaut riding a horse",
|
prompt: "a photograph of an astronaut riding a horse",
|
||||||
"width": 128,
|
width: 128,
|
||||||
"height": 128,
|
height: 128,
|
||||||
"num_inference_steps": 10,
|
num_inference_steps: 10,
|
||||||
"show_only_filtered_image": false,
|
show_only_filtered_image: false,
|
||||||
//"use_face_correction": 'GFPGANv1.3',
|
//"use_face_correction": 'GFPGANv1.3',
|
||||||
"use_upscale": "RealESRGAN_x4plus",
|
use_upscale: "RealESRGAN_x4plus",
|
||||||
"session_id": JASMINE_SESSION_ID,
|
session_id: JASMINE_SESSION_ID
|
||||||
})
|
})
|
||||||
await renderTask.enqueue(function(event) {
|
await renderTask.enqueue(function(event) {
|
||||||
console.log(this, event)
|
console.log(this, event)
|
||||||
if ('update' in event) {
|
if ("update" in event) {
|
||||||
const stepUpdate = event.update
|
const stepUpdate = event.update
|
||||||
if (complete || (stepUpdate.status && stepUpdate.step === stepUpdate.total_steps)) {
|
if (complete || (stepUpdate.status && stepUpdate.step === stepUpdate.total_steps)) {
|
||||||
expect(stepUpdate.status).toBe('succeeded')
|
expect(stepUpdate.status).toBe("succeeded")
|
||||||
expect(stepUpdate.output).toHaveSize(2)
|
expect(stepUpdate.output).toHaveSize(2)
|
||||||
} else {
|
} else {
|
||||||
expect(stepUpdate.step).toBe(stepCount)
|
expect(stepUpdate.step).toBe(stepCount)
|
||||||
@ -364,12 +382,12 @@ describe('stable-diffusion-ui', function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
console.log(renderTask.result)
|
console.log(renderTask.result)
|
||||||
expect(renderTask.result.status).toBe('succeeded')
|
expect(renderTask.result.status).toBe("succeeded")
|
||||||
expect(renderTask.result.output).toHaveSize(2)
|
expect(renderTask.result.output).toHaveSize(2)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('# Special cases', function() {
|
describe("# Special cases", function() {
|
||||||
it('should throw an exception on set for invalid sessionId', function() {
|
it("should throw an exception on set for invalid sessionId", function() {
|
||||||
expect(function() {
|
expect(function() {
|
||||||
SD.sessionId = undefined
|
SD.sessionId = undefined
|
||||||
}).toThrowError("Can't set sessionId to undefined.")
|
}).toThrowError("Can't set sessionId to undefined.")
|
||||||
@ -386,16 +404,17 @@ if (!PLUGINS.SELFTEST) {
|
|||||||
PLUGINS.SELFTEST = {}
|
PLUGINS.SELFTEST = {}
|
||||||
}
|
}
|
||||||
loadUIPlugins().then(function() {
|
loadUIPlugins().then(function() {
|
||||||
console.log('loadCompleted', loadEvent)
|
console.log("loadCompleted", loadEvent)
|
||||||
describe('@Plugins', function() {
|
describe("@Plugins", function() {
|
||||||
it('exposes hooks to overide', function() {
|
it("exposes hooks to overide", function() {
|
||||||
expect(typeof PLUGINS.IMAGE_INFO_BUTTONS).toBe('object')
|
expect(typeof PLUGINS.IMAGE_INFO_BUTTONS).toBe("object")
|
||||||
expect(typeof PLUGINS.TASK_CREATE).toBe('object')
|
expect(typeof PLUGINS.TASK_CREATE).toBe("object")
|
||||||
})
|
})
|
||||||
describe('supports selftests', function() { // Hook to allow plugins to define tests.
|
describe("supports selftests", function() {
|
||||||
|
// Hook to allow plugins to define tests.
|
||||||
const pluginsTests = Object.keys(PLUGINS.SELFTEST).filter((key) => PLUGINS.SELFTEST.hasOwnProperty(key))
|
const pluginsTests = Object.keys(PLUGINS.SELFTEST).filter((key) => PLUGINS.SELFTEST.hasOwnProperty(key))
|
||||||
if (!pluginsTests || pluginsTests.length <= 0) {
|
if (!pluginsTests || pluginsTests.length <= 0) {
|
||||||
it('but nothing loaded...', function() {
|
it("but nothing loaded...", function() {
|
||||||
expect(true).toBeTruthy()
|
expect(true).toBeTruthy()
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
(function() {
|
;(function() {
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
///////////////////// Function section
|
///////////////////// Function section
|
||||||
@ -18,49 +18,54 @@
|
|||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
function getCurrentTime() {
|
function getCurrentTime() {
|
||||||
const now = new Date();
|
const now = new Date()
|
||||||
let hours = now.getHours();
|
let hours = now.getHours()
|
||||||
let minutes = now.getMinutes();
|
let minutes = now.getMinutes()
|
||||||
let seconds = now.getSeconds();
|
let seconds = now.getSeconds()
|
||||||
|
|
||||||
hours = hours < 10 ? `0${hours}` : hours;
|
hours = hours < 10 ? `0${hours}` : hours
|
||||||
minutes = minutes < 10 ? `0${minutes}` : minutes;
|
minutes = minutes < 10 ? `0${minutes}` : minutes
|
||||||
seconds = seconds < 10 ? `0${seconds}` : seconds;
|
seconds = seconds < 10 ? `0${seconds}` : seconds
|
||||||
|
|
||||||
return `${hours}:${minutes}:${seconds}`;
|
return `${hours}:${minutes}:${seconds}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLogMessage(message) {
|
function addLogMessage(message) {
|
||||||
const logContainer = document.getElementById('merge-log');
|
const logContainer = document.getElementById("merge-log")
|
||||||
logContainer.innerHTML += `<i>${getCurrentTime()}</i> ${message}<br>`;
|
logContainer.innerHTML += `<i>${getCurrentTime()}</i> ${message}<br>`
|
||||||
|
|
||||||
// Scroll to the bottom of the log
|
// Scroll to the bottom of the log
|
||||||
logContainer.scrollTop = logContainer.scrollHeight;
|
logContainer.scrollTop = logContainer.scrollHeight
|
||||||
|
|
||||||
document.querySelector('#merge-log-container').style.display = 'block'
|
document.querySelector("#merge-log-container").style.display = "block"
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLogSeparator() {
|
function addLogSeparator() {
|
||||||
const logContainer = document.getElementById('merge-log');
|
const logContainer = document.getElementById("merge-log")
|
||||||
logContainer.innerHTML += '<hr>'
|
logContainer.innerHTML += "<hr>"
|
||||||
|
|
||||||
logContainer.scrollTop = logContainer.scrollHeight;
|
logContainer.scrollTop = logContainer.scrollHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawDiagram(fn) {
|
function drawDiagram(fn) {
|
||||||
const SIZE = 300
|
const SIZE = 300
|
||||||
const canvas = document.getElementById('merge-canvas');
|
const canvas = document.getElementById("merge-canvas")
|
||||||
canvas.height = canvas.width = SIZE
|
canvas.height = canvas.width = SIZE
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext("2d")
|
||||||
|
|
||||||
// Draw coordinate system
|
// Draw coordinate system
|
||||||
ctx.scale(1, -1);
|
ctx.scale(1, -1)
|
||||||
ctx.translate(0, -canvas.height);
|
ctx.translate(0, -canvas.height)
|
||||||
ctx.lineWidth = 1;
|
ctx.lineWidth = 1
|
||||||
ctx.beginPath();
|
ctx.beginPath()
|
||||||
|
|
||||||
ctx.strokeStyle = 'white'
|
ctx.strokeStyle = "white"
|
||||||
ctx.moveTo(0,0); ctx.lineTo(0,SIZE); ctx.lineTo(SIZE,SIZE); ctx.lineTo(SIZE,0); ctx.lineTo(0,0); ctx.lineTo(SIZE,SIZE);
|
ctx.moveTo(0, 0)
|
||||||
|
ctx.lineTo(0, SIZE)
|
||||||
|
ctx.lineTo(SIZE, SIZE)
|
||||||
|
ctx.lineTo(SIZE, 0)
|
||||||
|
ctx.lineTo(0, 0)
|
||||||
|
ctx.lineTo(SIZE, SIZE)
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.setLineDash([1, 2])
|
ctx.setLineDash([1, 2])
|
||||||
@ -74,29 +79,29 @@
|
|||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.setLineDash([])
|
ctx.setLineDash([])
|
||||||
ctx.beginPath();
|
ctx.beginPath()
|
||||||
ctx.strokeStyle = 'black'
|
ctx.strokeStyle = "black"
|
||||||
ctx.lineWidth = 3;
|
ctx.lineWidth = 3
|
||||||
// Plot function
|
// Plot function
|
||||||
const numSamples = 20;
|
const numSamples = 20
|
||||||
for (let i = 0; i <= numSamples; i++) {
|
for (let i = 0; i <= numSamples; i++) {
|
||||||
const x = i / numSamples;
|
const x = i / numSamples
|
||||||
const y = fn(x);
|
const y = fn(x)
|
||||||
|
|
||||||
const canvasX = x * SIZE;
|
const canvasX = x * SIZE
|
||||||
const canvasY = y * SIZE;
|
const canvasY = y * SIZE
|
||||||
|
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
ctx.moveTo(canvasX, canvasY);
|
ctx.moveTo(canvasX, canvasY)
|
||||||
} else {
|
} else {
|
||||||
ctx.lineTo(canvasX, canvasY);
|
ctx.lineTo(canvasX, canvasY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
// Plot alpha values (yellow boxes)
|
// Plot alpha values (yellow boxes)
|
||||||
let start = parseFloat( document.querySelector('#merge-start').value )
|
let start = parseFloat(document.querySelector("#merge-start").value)
|
||||||
let step = parseFloat( document.querySelector('#merge-step').value )
|
let step = parseFloat(document.querySelector("#merge-step").value)
|
||||||
let iterations = document.querySelector('#merge-count').value>>0
|
let iterations = document.querySelector("#merge-count").value >> 0
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.fillStyle = "yellow"
|
ctx.fillStyle = "yellow"
|
||||||
for (let i = 0; i < iterations; i++) {
|
for (let i = 0; i < iterations; i++) {
|
||||||
@ -107,33 +112,38 @@
|
|||||||
ctx.rect(x - 3, y - 3, 6, 6)
|
ctx.rect(x - 3, y - 3, 6, 6)
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
} else {
|
} else {
|
||||||
ctx.strokeStyle = 'red'
|
ctx.strokeStyle = "red"
|
||||||
ctx.moveTo(0,0); ctx.lineTo(0,SIZE); ctx.lineTo(SIZE,SIZE); ctx.lineTo(SIZE,0); ctx.lineTo(0,0); ctx.lineTo(SIZE,SIZE);
|
ctx.moveTo(0, 0)
|
||||||
|
ctx.lineTo(0, SIZE)
|
||||||
|
ctx.lineTo(SIZE, SIZE)
|
||||||
|
ctx.lineTo(SIZE, 0)
|
||||||
|
ctx.lineTo(0, 0)
|
||||||
|
ctx.lineTo(SIZE, SIZE)
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
addLogMessage('<i>Warning: maximum ratio is ≥ 100%</i>')
|
addLogMessage("<i>Warning: maximum ratio is ≥ 100%</i>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateChart() {
|
function updateChart() {
|
||||||
let fn = (x) => x
|
let fn = (x) => x
|
||||||
switch (document.querySelector('#merge-interpolation').value) {
|
switch (document.querySelector("#merge-interpolation").value) {
|
||||||
case 'SmoothStep':
|
case "SmoothStep":
|
||||||
fn = smoothstep
|
fn = smoothstep
|
||||||
break
|
break
|
||||||
case 'SmootherStep':
|
case "SmootherStep":
|
||||||
fn = smootherstep
|
fn = smootherstep
|
||||||
break
|
break
|
||||||
case 'SmoothestStep':
|
case "SmoothestStep":
|
||||||
fn = smootheststep
|
fn = smootheststep
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
drawDiagram(fn)
|
drawDiagram(fn)
|
||||||
}
|
}
|
||||||
createTab({
|
createTab({
|
||||||
id: 'merge',
|
id: "merge",
|
||||||
icon: 'fa-code-merge',
|
icon: "fa-code-merge",
|
||||||
label: 'Merge models',
|
label: "Merge models",
|
||||||
css: `
|
css: `
|
||||||
#tab-content-merge .tab-content-inner {
|
#tab-content-merge .tab-content-inner {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@ -307,19 +317,19 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabSettingsSingle = document.querySelector('#tab-merge-opts-single')
|
const tabSettingsSingle = document.querySelector("#tab-merge-opts-single")
|
||||||
const tabSettingsBatch = document.querySelector('#tab-merge-opts-batch')
|
const tabSettingsBatch = document.querySelector("#tab-merge-opts-batch")
|
||||||
linkTabContents(tabSettingsSingle)
|
linkTabContents(tabSettingsSingle)
|
||||||
linkTabContents(tabSettingsBatch)
|
linkTabContents(tabSettingsBatch)
|
||||||
|
|
||||||
console.log('Activate')
|
console.log("Activate")
|
||||||
let mergeModelAField = new ModelDropdown(document.querySelector('#mergeModelA'), 'stable-diffusion')
|
let mergeModelAField = new ModelDropdown(document.querySelector("#mergeModelA"), "stable-diffusion")
|
||||||
let mergeModelBField = new ModelDropdown(document.querySelector('#mergeModelB'), 'stable-diffusion')
|
let mergeModelBField = new ModelDropdown(document.querySelector("#mergeModelB"), "stable-diffusion")
|
||||||
updateChart()
|
updateChart()
|
||||||
|
|
||||||
// slider
|
// slider
|
||||||
const singleMergeRatioField = document.querySelector('#single-merge-ratio')
|
const singleMergeRatioField = document.querySelector("#single-merge-ratio")
|
||||||
const singleMergeRatioSlider = document.querySelector('#single-merge-ratio-slider')
|
const singleMergeRatioSlider = document.querySelector("#single-merge-ratio-slider")
|
||||||
|
|
||||||
function updateSingleMergeRatio() {
|
function updateSingleMergeRatio() {
|
||||||
singleMergeRatioField.value = singleMergeRatioSlider.value / 10
|
singleMergeRatioField.value = singleMergeRatioSlider.value / 10
|
||||||
@ -337,21 +347,21 @@
|
|||||||
singleMergeRatioSlider.dispatchEvent(new Event("change"))
|
singleMergeRatioSlider.dispatchEvent(new Event("change"))
|
||||||
}
|
}
|
||||||
|
|
||||||
singleMergeRatioSlider.addEventListener('input', updateSingleMergeRatio)
|
singleMergeRatioSlider.addEventListener("input", updateSingleMergeRatio)
|
||||||
singleMergeRatioField.addEventListener('input', updateSingleMergeRatioSlider)
|
singleMergeRatioField.addEventListener("input", updateSingleMergeRatioSlider)
|
||||||
updateSingleMergeRatio()
|
updateSingleMergeRatio()
|
||||||
|
|
||||||
document.querySelector('.merge-config').addEventListener('change', updateChart)
|
document.querySelector(".merge-config").addEventListener("change", updateChart)
|
||||||
|
|
||||||
document.querySelector('#merge-button').addEventListener('click', async function(e) {
|
document.querySelector("#merge-button").addEventListener("click", async function(e) {
|
||||||
// Build request template
|
// Build request template
|
||||||
let model0 = mergeModelAField.value
|
let model0 = mergeModelAField.value
|
||||||
let model1 = mergeModelBField.value
|
let model1 = mergeModelBField.value
|
||||||
let request = { model0: model0, model1: model1 }
|
let request = { model0: model0, model1: model1 }
|
||||||
request['use_fp16'] = document.querySelector('#merge-fp').value == 'fp16'
|
request["use_fp16"] = document.querySelector("#merge-fp").value == "fp16"
|
||||||
let iterations = document.querySelector('#merge-count').value>>0
|
let iterations = document.querySelector("#merge-count").value >> 0
|
||||||
let start = parseFloat( document.querySelector('#merge-start').value )
|
let start = parseFloat(document.querySelector("#merge-start").value)
|
||||||
let step = parseFloat( document.querySelector('#merge-step').value )
|
let step = parseFloat(document.querySelector("#merge-step").value)
|
||||||
|
|
||||||
if (isTabActive(tabSettingsSingle)) {
|
if (isTabActive(tabSettingsSingle)) {
|
||||||
start = parseFloat(singleMergeRatioField.value)
|
start = parseFloat(singleMergeRatioField.value)
|
||||||
@ -364,27 +374,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (start + (iterations - 1) * step >= 100) {
|
if (start + (iterations - 1) * step >= 100) {
|
||||||
addLogMessage('<i>Aborting: maximum ratio is ≥ 100%</i>')
|
addLogMessage("<i>Aborting: maximum ratio is ≥ 100%</i>")
|
||||||
addLogMessage('Reduce the number of variations or the step size')
|
addLogMessage("Reduce the number of variations or the step size")
|
||||||
addLogSeparator()
|
addLogSeparator()
|
||||||
document.querySelector('#merge-count').focus()
|
document.querySelector("#merge-count").focus()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.querySelector('#merge-filename').value == "") {
|
if (document.querySelector("#merge-filename").value == "") {
|
||||||
addLogMessage('<i>Aborting: No output file name specified</i>')
|
addLogMessage("<i>Aborting: No output file name specified</i>")
|
||||||
addLogSeparator()
|
addLogSeparator()
|
||||||
document.querySelector('#merge-filename').focus()
|
document.querySelector("#merge-filename").focus()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable merge button
|
// Disable merge button
|
||||||
e.target.disabled = true
|
e.target.disabled = true
|
||||||
e.target.classList.add('disabled')
|
e.target.classList.add("disabled")
|
||||||
let cursor = $("body").css("cursor");
|
let cursor = $("body").css("cursor")
|
||||||
let label = document.querySelector('#merge-button').innerHTML
|
let label = document.querySelector("#merge-button").innerHTML
|
||||||
$("body").css("cursor", "progress");
|
$("body").css("cursor", "progress")
|
||||||
document.querySelector('#merge-button').innerHTML = 'Merging models ...'
|
document.querySelector("#merge-button").innerHTML = "Merging models ..."
|
||||||
|
|
||||||
addLogMessage("Merging models")
|
addLogMessage("Merging models")
|
||||||
addLogMessage("Model A: " + model0)
|
addLogMessage("Model A: " + model0)
|
||||||
@ -393,46 +403,48 @@
|
|||||||
// Batch main loop
|
// Batch main loop
|
||||||
for (let i = 0; i < iterations; i++) {
|
for (let i = 0; i < iterations; i++) {
|
||||||
let alpha = (start + i * step) / 100
|
let alpha = (start + i * step) / 100
|
||||||
switch (document.querySelector('#merge-interpolation').value) {
|
switch (document.querySelector("#merge-interpolation").value) {
|
||||||
case 'SmoothStep':
|
case "SmoothStep":
|
||||||
alpha = smoothstep(alpha)
|
alpha = smoothstep(alpha)
|
||||||
break
|
break
|
||||||
case 'SmootherStep':
|
case "SmootherStep":
|
||||||
alpha = smootherstep(alpha)
|
alpha = smootherstep(alpha)
|
||||||
break
|
break
|
||||||
case 'SmoothestStep':
|
case "SmoothestStep":
|
||||||
alpha = smootheststep(alpha)
|
alpha = smootheststep(alpha)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
addLogMessage(`merging batch job ${i + 1}/${iterations}, alpha = ${alpha.toFixed(5)}...`)
|
addLogMessage(`merging batch job ${i + 1}/${iterations}, alpha = ${alpha.toFixed(5)}...`)
|
||||||
|
|
||||||
request['out_path'] = document.querySelector('#merge-filename').value
|
request["out_path"] = document.querySelector("#merge-filename").value
|
||||||
request['out_path'] += '-' + alpha.toFixed(5) + '.' + document.querySelector('#merge-format').value
|
request["out_path"] += "-" + alpha.toFixed(5) + "." + document.querySelector("#merge-format").value
|
||||||
addLogMessage(` filename: ${request['out_path']}`)
|
addLogMessage(` filename: ${request["out_path"]}`)
|
||||||
|
|
||||||
request['ratio'] = alpha
|
request["ratio"] = alpha
|
||||||
let res = await fetch('/model/merge', {
|
let res = await fetch("/model/merge", {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify(request) })
|
body: JSON.stringify(request)
|
||||||
const data = await res.json();
|
})
|
||||||
|
const data = await res.json()
|
||||||
addLogMessage(JSON.stringify(data))
|
addLogMessage(JSON.stringify(data))
|
||||||
}
|
}
|
||||||
addLogMessage("<b>Done.</b> The models have been saved to your <tt>models/stable-diffusion</tt> folder.")
|
addLogMessage(
|
||||||
|
"<b>Done.</b> The models have been saved to your <tt>models/stable-diffusion</tt> folder."
|
||||||
|
)
|
||||||
addLogSeparator()
|
addLogSeparator()
|
||||||
// Re-enable merge button
|
// Re-enable merge button
|
||||||
$("body").css("cursor", cursor);
|
$("body").css("cursor", cursor)
|
||||||
document.querySelector('#merge-button').innerHTML = label
|
document.querySelector("#merge-button").innerHTML = label
|
||||||
e.target.disabled = false
|
e.target.disabled = false
|
||||||
e.target.classList.remove('disabled')
|
e.target.classList.remove("disabled")
|
||||||
|
|
||||||
// Update model list
|
// Update model list
|
||||||
stableDiffusionModelField.innerHTML = ''
|
stableDiffusionModelField.innerHTML = ""
|
||||||
vaeModelField.innerHTML = ''
|
vaeModelField.innerHTML = ""
|
||||||
hypernetworkModelField.innerHTML = ''
|
hypernetworkModelField.innerHTML = ""
|
||||||
await getModels()
|
await getModels()
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
})()
|
})()
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
(function () {
|
;(function() {
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
var styleSheet = document.createElement("style");
|
var styleSheet = document.createElement("style")
|
||||||
styleSheet.textContent = `
|
styleSheet.textContent = `
|
||||||
.modifier-card-tiny.modifier-toggle-inactive {
|
.modifier-card-tiny.modifier-toggle-inactive {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 2px dashed red;
|
border: 2px dashed red;
|
||||||
opacity:0.2;
|
opacity:0.2;
|
||||||
}
|
}
|
||||||
`;
|
`
|
||||||
document.head.appendChild(styleSheet);
|
document.head.appendChild(styleSheet)
|
||||||
|
|
||||||
// observe for changes in tag list
|
// observe for changes in tag list
|
||||||
var observer = new MutationObserver(function(mutations) {
|
var observer = new MutationObserver(function(mutations) {
|
||||||
@ -25,28 +25,28 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
function ModifierToggle() {
|
function ModifierToggle() {
|
||||||
let overlays = document.querySelector('#editor-inputs-tags-list').querySelectorAll('.modifier-card-overlay')
|
let overlays = document.querySelector("#editor-inputs-tags-list").querySelectorAll(".modifier-card-overlay")
|
||||||
overlays.forEach (i => {
|
overlays.forEach((i) => {
|
||||||
i.oncontextmenu = (e) => {
|
i.oncontextmenu = (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (i.parentElement.classList.contains('modifier-toggle-inactive')) {
|
if (i.parentElement.classList.contains("modifier-toggle-inactive")) {
|
||||||
i.parentElement.classList.remove('modifier-toggle-inactive')
|
i.parentElement.classList.remove("modifier-toggle-inactive")
|
||||||
}
|
} else {
|
||||||
else
|
i.parentElement.classList.add("modifier-toggle-inactive")
|
||||||
{
|
|
||||||
i.parentElement.classList.add('modifier-toggle-inactive')
|
|
||||||
}
|
}
|
||||||
// refresh activeTags
|
// refresh activeTags
|
||||||
let modifierName = i.parentElement.getElementsByClassName('modifier-card-label')[0].getElementsByTagName("p")[0].dataset.fullName
|
let modifierName = i.parentElement
|
||||||
activeTags = activeTags.map(obj => {
|
.getElementsByClassName("modifier-card-label")[0]
|
||||||
|
.getElementsByTagName("p")[0].dataset.fullName
|
||||||
|
activeTags = activeTags.map((obj) => {
|
||||||
if (trimModifiers(obj.name) === trimModifiers(modifierName)) {
|
if (trimModifiers(obj.name) === trimModifiers(modifierName)) {
|
||||||
return {...obj, inactive: (obj.element.classList.contains('modifier-toggle-inactive'))};
|
return { ...obj, inactive: obj.element.classList.contains("modifier-toggle-inactive") }
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj
|
||||||
});
|
})
|
||||||
document.dispatchEvent(new Event('refreshImageModifiers'))
|
document.dispatchEvent(new Event("refreshImageModifiers"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
(function() {
|
;(function() {
|
||||||
// Register selftests when loaded by jasmine.
|
// Register selftests when loaded by jasmine.
|
||||||
if (typeof PLUGINS?.SELFTEST === 'object') {
|
if (typeof PLUGINS?.SELFTEST === "object") {
|
||||||
PLUGINS.SELFTEST["release-notes"] = function() {
|
PLUGINS.SELFTEST["release-notes"] = function() {
|
||||||
it('should be able to fetch CHANGES.md', async function() {
|
it("should be able to fetch CHANGES.md", async function() {
|
||||||
let releaseNotes = await fetch(`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/main/CHANGES.md`)
|
let releaseNotes = await fetch(
|
||||||
|
`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/main/CHANGES.md`
|
||||||
|
)
|
||||||
expect(releaseNotes.status).toBe(200)
|
expect(releaseNotes.status).toBe(200)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createTab({
|
createTab({
|
||||||
id: 'news',
|
id: "news",
|
||||||
icon: 'fa-bolt',
|
icon: "fa-bolt",
|
||||||
label: "What's new",
|
label: "What's new",
|
||||||
css: `
|
css: `
|
||||||
#tab-content-news .tab-content-inner {
|
#tab-content-news .tab-content-inner {
|
||||||
@ -22,20 +24,22 @@
|
|||||||
`,
|
`,
|
||||||
onOpen: async ({ firstOpen }) => {
|
onOpen: async ({ firstOpen }) => {
|
||||||
if (firstOpen) {
|
if (firstOpen) {
|
||||||
const loadMarkedScriptPromise = loadScript('/media/js/marked.min.js')
|
const loadMarkedScriptPromise = loadScript("/media/js/marked.min.js")
|
||||||
|
|
||||||
let appConfig = await fetch('/get/app_config')
|
let appConfig = await fetch("/get/app_config")
|
||||||
if (!appConfig.ok) {
|
if (!appConfig.ok) {
|
||||||
console.error('[release-notes] Failed to get app_config.')
|
console.error("[release-notes] Failed to get app_config.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
appConfig = await appConfig.json()
|
appConfig = await appConfig.json()
|
||||||
|
|
||||||
const updateBranch = appConfig.update_branch || 'main'
|
const updateBranch = appConfig.update_branch || "main"
|
||||||
|
|
||||||
let releaseNotes = await fetch(`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/${updateBranch}/CHANGES.md`)
|
let releaseNotes = await fetch(
|
||||||
|
`https://raw.githubusercontent.com/cmdr2/stable-diffusion-ui/${updateBranch}/CHANGES.md`
|
||||||
|
)
|
||||||
if (!releaseNotes.ok) {
|
if (!releaseNotes.ok) {
|
||||||
console.error('[release-notes] Failed to get CHANGES.md.')
|
console.error("[release-notes] Failed to get CHANGES.md.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
releaseNotes = await releaseNotes.text()
|
releaseNotes = await releaseNotes.text()
|
||||||
@ -44,6 +48,6 @@
|
|||||||
|
|
||||||
return marked.parse(releaseNotes)
|
return marked.parse(releaseNotes)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
})()
|
})()
|
@ -1,6 +1,7 @@
|
|||||||
/* SD-UI Selftest Plugin.js
|
/* SD-UI Selftest Plugin.js
|
||||||
*/
|
*/
|
||||||
(function() { "use strict"
|
;(function() {
|
||||||
|
"use strict"
|
||||||
const ID_PREFIX = "selftest-plugin"
|
const ID_PREFIX = "selftest-plugin"
|
||||||
|
|
||||||
const links = document.getElementById("community-links")
|
const links = document.getElementById("community-links")
|
||||||
@ -10,16 +11,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add link to Jasmine SpecRunner
|
// Add link to Jasmine SpecRunner
|
||||||
const pluginLink = document.createElement('li')
|
const pluginLink = document.createElement("li")
|
||||||
const options = {
|
const options = {
|
||||||
'stopSpecOnExpectationFailure': "true",
|
stopSpecOnExpectationFailure: "true",
|
||||||
'stopOnSpecFailure': 'false',
|
stopOnSpecFailure: "false",
|
||||||
'random': 'false',
|
random: "false",
|
||||||
'hideDisabled': 'false'
|
hideDisabled: "false"
|
||||||
}
|
}
|
||||||
const optStr = Object.entries(options).map(([key, val]) => `${key}=${val}`).join('&')
|
const optStr = Object.entries(options)
|
||||||
|
.map(([key, val]) => `${key}=${val}`)
|
||||||
|
.join("&")
|
||||||
pluginLink.innerHTML = `<a id="${ID_PREFIX}-starttest" href="${location.protocol}/plugins/core/SpecRunner.html?${optStr}" target="_blank"><i class="fa-solid fa-vial-circle-check"></i> Start SelfTest</a>`
|
pluginLink.innerHTML = `<a id="${ID_PREFIX}-starttest" href="${location.protocol}/plugins/core/SpecRunner.html?${optStr}" target="_blank"><i class="fa-solid fa-vial-circle-check"></i> Start SelfTest</a>`
|
||||||
links.appendChild(pluginLink)
|
links.appendChild(pluginLink)
|
||||||
|
|
||||||
console.log('%s loaded!', ID_PREFIX)
|
console.log("%s loaded!", ID_PREFIX)
|
||||||
})()
|
})()
|
||||||
|
8
yarn.lock
Normal file
8
yarn.lock
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
prettier@^1.19.1:
|
||||||
|
version "1.19.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
||||||
|
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
Loading…
Reference in New Issue
Block a user