forked from extern/easydiffusion
commit
8e57c49043
@ -21,6 +21,7 @@
|
|||||||
- A `What's New?` tab in the UI
|
- A `What's New?` tab in the UI
|
||||||
|
|
||||||
### Detailed changelog
|
### Detailed changelog
|
||||||
|
* 2.4.13 - 21 Nov 2022 - Change the modifier weight via mouse wheel, drag to reorder selected modifiers, and some more modifier-related fixes. Thanks @patriceac
|
||||||
* 2.4.12 - 21 Nov 2022 - Another fix for improving how long images take to generate. Reduces the time taken for an enqueued task to start processing.
|
* 2.4.12 - 21 Nov 2022 - Another fix for improving how long images take to generate. Reduces the time taken for an enqueued task to start processing.
|
||||||
* 2.4.11 - 21 Nov 2022 - Installer improvements: avoid crashing if the username contains a space or special characters, allow moving/renaming the folder after installation on Windows, whitespace fix on git apply
|
* 2.4.11 - 21 Nov 2022 - Installer improvements: avoid crashing if the username contains a space or special characters, allow moving/renaming the folder after installation on Windows, whitespace fix on git apply
|
||||||
* 2.4.11 - 21 Nov 2022 - Validate inputs before submitting the Image request
|
* 2.4.11 - 21 Nov 2022 - Validate inputs before submitting the Image request
|
||||||
|
1
NSIS/README.md
Normal file
1
NSIS/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
Scripts to be used with the Nullsoft Scriptable Installation System
|
BIN
NSIS/astro.bmp
Normal file
BIN
NSIS/astro.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 288 KiB |
BIN
NSIS/sd.ico
Normal file
BIN
NSIS/sd.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 200 KiB |
265
NSIS/sdui.nsi
Normal file
265
NSIS/sdui.nsi
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
; Script generated by the HM NIS Edit Script Wizard.
|
||||||
|
|
||||||
|
Target x86-unicode
|
||||||
|
Unicode True
|
||||||
|
!AddPluginDir /x86-unicode "."
|
||||||
|
; HM NIS Edit Wizard helper defines
|
||||||
|
!define PRODUCT_NAME "Stable Diffusion UI"
|
||||||
|
!define PRODUCT_VERSION "Installer 2.35"
|
||||||
|
!define PRODUCT_PUBLISHER "cmdr2 and contributors"
|
||||||
|
!define PRODUCT_WEB_SITE "https://stable-diffusion-ui.github.io"
|
||||||
|
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Cmdr2\App Paths\installer.exe"
|
||||||
|
|
||||||
|
; MUI 1.67 compatible ------
|
||||||
|
!include "MUI.nsh"
|
||||||
|
!include "LogicLib.nsh"
|
||||||
|
!include "nsDialogs.nsh"
|
||||||
|
|
||||||
|
Var Dialog
|
||||||
|
Var Label
|
||||||
|
Var Button
|
||||||
|
|
||||||
|
Var InstDirLen
|
||||||
|
Var LongPathsEnabled
|
||||||
|
Var AccountType
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------------------------------------------
|
||||||
|
; This function returns the number of spaces in a string.
|
||||||
|
; The string is passed on the stack (using Push $STRING)
|
||||||
|
; The result is also returned on the stack and can be consumed with Pop $var
|
||||||
|
; https://nsis.sourceforge.io/Check_for_spaces_in_a_directory_path
|
||||||
|
Function CheckForSpaces
|
||||||
|
Exch $R0
|
||||||
|
Push $R1
|
||||||
|
Push $R2
|
||||||
|
Push $R3
|
||||||
|
StrCpy $R1 -1
|
||||||
|
StrCpy $R3 $R0
|
||||||
|
StrCpy $R0 0
|
||||||
|
loop:
|
||||||
|
StrCpy $R2 $R3 1 $R1
|
||||||
|
IntOp $R1 $R1 - 1
|
||||||
|
StrCmp $R2 "" done
|
||||||
|
StrCmp $R2 " " 0 loop
|
||||||
|
IntOp $R0 $R0 + 1
|
||||||
|
Goto loop
|
||||||
|
done:
|
||||||
|
Pop $R3
|
||||||
|
Pop $R2
|
||||||
|
Pop $R1
|
||||||
|
Exch $R0
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------------------------------------------
|
||||||
|
; The function DirectoryLeave is called after the user chose the installation directory.
|
||||||
|
; If it calls "abort", the user is sent back to choose a different directory.
|
||||||
|
Function DirectoryLeave
|
||||||
|
; check whether the installation directory path is longer than 30 characters.
|
||||||
|
; If yes, we suggest to the user to enable long filename support
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
StrLen $InstDirLen "$INSTDIR"
|
||||||
|
|
||||||
|
; Check whether the registry key that allows for >260 characters in a path name is set
|
||||||
|
ReadRegStr $LongPathsEnabled HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled"
|
||||||
|
|
||||||
|
${If} $InstDirLen > 30
|
||||||
|
${AndIf} $LongPathsEnabled == "0"
|
||||||
|
; Check whether we're in the Admin group
|
||||||
|
UserInfo::GetAccountType
|
||||||
|
Pop $AccountType
|
||||||
|
|
||||||
|
${If} $AccountType == "Admin"
|
||||||
|
${AndIf} ${Cmd} `MessageBox MB_YESNO|MB_ICONQUESTION 'The path name is too long. $\n$\nYou can either enable long file name support in Windows,$\nor you can go back and choose a different path.$\n$\nFor details see: shorturl.at/auBD1$\n$\nEnable long path name support in Windows?' IDYES`
|
||||||
|
; Enable long path names
|
||||||
|
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1
|
||||||
|
${Else}
|
||||||
|
MessageBox MB_OK|MB_ICONEXCLAMATION "Installation path name too long. The installation path must not have more than 30 characters."
|
||||||
|
abort
|
||||||
|
${EndIf}
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
; Check for spaces in the installation directory path.
|
||||||
|
; ----------------------------------------------------
|
||||||
|
|
||||||
|
; $R0 = CheckForSpaces( $INSTDIR )
|
||||||
|
Push $INSTDIR # Input string (install path).
|
||||||
|
Call CheckForSpaces
|
||||||
|
Pop $R0 # The function returns the number of spaces found in the input string.
|
||||||
|
|
||||||
|
; Check if any spaces exist in $INSTDIR.
|
||||||
|
${If} $R0 != 0
|
||||||
|
; Plural if more than 1 space in $INSTDIR.
|
||||||
|
; If $R0 == 1: $R1 = ""; else: $R1 = "s"
|
||||||
|
StrCmp $R0 1 0 +3
|
||||||
|
StrCpy $R1 ""
|
||||||
|
Goto +2
|
||||||
|
StrCpy $R1 "s"
|
||||||
|
|
||||||
|
; Show message box then take the user back to the Directory page.
|
||||||
|
MessageBox MB_OK|MB_ICONEXCLAMATION "Error: The Installaton directory \
|
||||||
|
has $R0 space character$R1.$\nPlease choose an installation directory without space characters."
|
||||||
|
Abort
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
; Check for NTFS filesystem. Installations on FAT fail.
|
||||||
|
; -----------------------------------------------------
|
||||||
|
StrCpy $5 $INSTDIR 3
|
||||||
|
System::Call 'Kernel32::GetVolumeInformation(t "$5",t,i ${NSIS_MAX_STRLEN},*i,*i,*i,t.r1,i ${NSIS_MAX_STRLEN})i.r0'
|
||||||
|
${If} $0 <> 0
|
||||||
|
${AndIf} $1 == "NTFS"
|
||||||
|
MessageBox mb_ok "$5 has filesystem type '$1'.$\nOnly NTFS filesystems are supported.$\nPlease choose a different drive."
|
||||||
|
Abort
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------------------------------------------
|
||||||
|
; Open the MS download page in a browser and enable the [Next] button
|
||||||
|
Function MSMediaFeaturepack
|
||||||
|
ExecShell "open" "https://www.microsoft.com/en-us/software-download/mediafeaturepack"
|
||||||
|
|
||||||
|
GetDlgItem $0 $HWNDPARENT 1
|
||||||
|
EnableWindow $0 1
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------------------------------------------
|
||||||
|
; Install the MS Media Feature Pack, if it is missing (e.g. on Windows 10 N)
|
||||||
|
Function MediaPackDialog
|
||||||
|
!insertmacro MUI_HEADER_TEXT "Windows Media Feature Pack" "Required software module is missing"
|
||||||
|
|
||||||
|
; Skip this dialog if mf.dll is installed
|
||||||
|
${If} ${FileExists} "$WINDIR\system32\mf.dll"
|
||||||
|
Abort
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
nsDialogs::Create 1018
|
||||||
|
Pop $Dialog
|
||||||
|
|
||||||
|
${If} $Dialog == error
|
||||||
|
Abort
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
${NSD_CreateLabel} 0 0 100% 48u "The Windows Media Feature Pack is missing on this computer. It is required for the Stable Diffusion UI.$\nYou can continue the installation after installing the Windows Media Feature Pack."
|
||||||
|
Pop $Label
|
||||||
|
|
||||||
|
${NSD_CreateButton} 10% 49u 80% 12u "Download Meda Feature Pack from Microsoft"
|
||||||
|
Pop $Button
|
||||||
|
|
||||||
|
GetFunctionAddress $0 MSMediaFeaturePack
|
||||||
|
nsDialogs::OnClick $Button $0
|
||||||
|
GetDlgItem $0 $HWNDPARENT 1
|
||||||
|
EnableWindow $0 0
|
||||||
|
nsDialogs::Show
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------------------------------------------
|
||||||
|
; MUI Settings
|
||||||
|
;---------------------------------------------------------------------------------------------------------
|
||||||
|
!define MUI_ABORTWARNING
|
||||||
|
!define MUI_ICON "sd.ico"
|
||||||
|
|
||||||
|
!define MUI_WELCOMEFINISHPAGE_BITMAP "astro.bmp"
|
||||||
|
|
||||||
|
; Welcome page
|
||||||
|
!define MUI_WELCOMEPAGE_TEXT "This installer will guide you through the installation of Stable Diffusion UI.$\n$\n\
|
||||||
|
Click Next to continue."
|
||||||
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
|
Page custom MediaPackDialog
|
||||||
|
|
||||||
|
; License page
|
||||||
|
!insertmacro MUI_PAGE_LICENSE "..\LICENSE"
|
||||||
|
!insertmacro MUI_PAGE_LICENSE "..\CreativeML Open RAIL-M License"
|
||||||
|
; Directory page
|
||||||
|
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE "DirectoryLeave"
|
||||||
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
|
|
||||||
|
; Instfiles page
|
||||||
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
|
||||||
|
; Finish page
|
||||||
|
!define MUI_FINISHPAGE_RUN "$INSTDIR\Start Stable Diffusion UI.cmd"
|
||||||
|
!insertmacro MUI_PAGE_FINISH
|
||||||
|
|
||||||
|
; Language files
|
||||||
|
!insertmacro MUI_LANGUAGE "English"
|
||||||
|
;---------------------------------------------------------------------------------------------------------
|
||||||
|
; MUI end
|
||||||
|
;---------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
|
||||||
|
OutFile "Install Stable Diffusion UI.exe"
|
||||||
|
InstallDir "C:\Stable-Diffusion-UI\"
|
||||||
|
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
|
||||||
|
ShowInstDetails show
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------------------------------------------
|
||||||
|
; List of files to be installed
|
||||||
|
Section "MainSection" SEC01
|
||||||
|
SetOutPath "$INSTDIR"
|
||||||
|
File "..\CreativeML Open RAIL-M License"
|
||||||
|
File "..\How to install and run.txt"
|
||||||
|
File "..\LICENSE"
|
||||||
|
File "..\Start Stable Diffusion UI.cmd"
|
||||||
|
SetOutPath "$INSTDIR\scripts"
|
||||||
|
File "..\scripts\bootstrap.bat"
|
||||||
|
File "..\scripts\install_status.txt"
|
||||||
|
File "..\scripts\on_env_start.bat"
|
||||||
|
File "C:\windows\system32\curl.exe"
|
||||||
|
CreateDirectory "$INSTDIR\profile"
|
||||||
|
CreateDirectory "$SMPROGRAMS\Stable Diffusion UI"
|
||||||
|
CreateShortCut "$SMPROGRAMS\Stable Diffusion UI\Start Stable Diffusion UI.lnk" "$INSTDIR\Start Stable Diffusion UI.cmd"
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
;---------------------------------------------------------------------------------------------------------
|
||||||
|
; Our installer only needs 25 KB, but once it has run, we need 25 GB
|
||||||
|
; So we need to overwrite the automatically detected space requirements.
|
||||||
|
; https://nsis.sourceforge.io/Docs/Chapter4.html#4.9.13.7
|
||||||
|
; The example in section 4.9.13.7 seems to be wrong: the number
|
||||||
|
; needs to be provided in Kilobytes.
|
||||||
|
Function .onInit
|
||||||
|
; Set required size of section 'SEC01' to 25 Gigabytes
|
||||||
|
SectionSetSize ${SEC01} 26214400
|
||||||
|
|
||||||
|
|
||||||
|
; Check system meory size. We need at least 8GB
|
||||||
|
; ----------------------------------------------------
|
||||||
|
|
||||||
|
; allocate a few bytes of memory
|
||||||
|
System::Alloc 64
|
||||||
|
Pop $1
|
||||||
|
|
||||||
|
; Retrieve HW info from the Windows Kernel
|
||||||
|
System::Call "*$1(i64)"
|
||||||
|
System::Call "Kernel32::GlobalMemoryStatusEx(i r1)"
|
||||||
|
; unpack the data into $R2 - $R10
|
||||||
|
System::Call "*$1(i.r2, i.r3, l.r4, l.r5, l.r6, l.r7, l.r8, l.r9, l.r10)"
|
||||||
|
|
||||||
|
# free up the memory
|
||||||
|
System::Free $1
|
||||||
|
|
||||||
|
; Result mapping:
|
||||||
|
; "Structure size: $2 bytes"
|
||||||
|
; "Memory load: $3%"
|
||||||
|
; "Total physical memory: $4 bytes"
|
||||||
|
; "Free physical memory: $5 bytes"
|
||||||
|
; "Total page file: $6 bytes"
|
||||||
|
; "Free page file: $7 bytes"
|
||||||
|
; "Total virtual: $8 bytes"
|
||||||
|
; "Free virtual: $9 bytes"
|
||||||
|
|
||||||
|
; Mem size in MB
|
||||||
|
System::Int64Op $4 / 1048576
|
||||||
|
Pop $4
|
||||||
|
|
||||||
|
${If} $4 < "8000"
|
||||||
|
MessageBox MB_OK|MB_ICONEXCLAMATION "Warning!$\n$\nYour system has less than 8GB of memory (RAM).$\n$\n\
|
||||||
|
You can still try to install Stable Diffusion UI,$\nbut it might have problems to start, or run$\nvery slowly."
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
|
||||||
|
;Section -Post
|
||||||
|
; WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\installer.exe"
|
||||||
|
;SectionEnd
|
@ -20,7 +20,7 @@
|
|||||||
<div id="container">
|
<div id="container">
|
||||||
<div id="top-nav">
|
<div id="top-nav">
|
||||||
<div id="logo">
|
<div id="logo">
|
||||||
<h1>Stable Diffusion UI <small>v2.4.12 <span id="updateBranchLabel"></span></small></h1>
|
<h1>Stable Diffusion UI <small>v2.4.13 <span id="updateBranchLabel"></span></small></h1>
|
||||||
</div>
|
</div>
|
||||||
<div id="server-status">
|
<div id="server-status">
|
||||||
<div id="server-status-color">●</div>
|
<div id="server-status-color">●</div>
|
||||||
|
@ -161,18 +161,7 @@ const TASK_MAPPING = {
|
|||||||
setUI: (use_stable_diffusion_model) => {
|
setUI: (use_stable_diffusion_model) => {
|
||||||
const oldVal = stableDiffusionModelField.value
|
const oldVal = stableDiffusionModelField.value
|
||||||
|
|
||||||
let pathIdx = use_stable_diffusion_model.lastIndexOf('/') // Linux, Mac paths
|
use_stable_diffusion_model = getModelPath(use_stable_diffusion_model, ['.ckpt'])
|
||||||
if (pathIdx < 0) {
|
|
||||||
pathIdx = use_stable_diffusion_model.lastIndexOf('\\') // Windows paths.
|
|
||||||
}
|
|
||||||
if (pathIdx >= 0) {
|
|
||||||
use_stable_diffusion_model = use_stable_diffusion_model.slice(pathIdx + 1)
|
|
||||||
}
|
|
||||||
const modelExt = '.ckpt'
|
|
||||||
if (use_stable_diffusion_model.endsWith(modelExt)) {
|
|
||||||
use_stable_diffusion_model = use_stable_diffusion_model.slice(0, use_stable_diffusion_model.length - modelExt.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
stableDiffusionModelField.value = use_stable_diffusion_model
|
stableDiffusionModelField.value = use_stable_diffusion_model
|
||||||
|
|
||||||
if (!stableDiffusionModelField.value) {
|
if (!stableDiffusionModelField.value) {
|
||||||
@ -182,6 +171,19 @@ const TASK_MAPPING = {
|
|||||||
readUI: () => stableDiffusionModelField.value,
|
readUI: () => stableDiffusionModelField.value,
|
||||||
parse: (val) => val
|
parse: (val) => val
|
||||||
},
|
},
|
||||||
|
use_vae_model: { name: 'VAE model',
|
||||||
|
setUI: (use_vae_model) => {
|
||||||
|
const oldVal = vaeModelField.value
|
||||||
|
|
||||||
|
if (use_vae_model !== '') {
|
||||||
|
use_vae_model = getModelPath(use_vae_model, ['.vae.pt', '.ckpt'])
|
||||||
|
use_vae_model = use_vae_model !== '' ? use_vae_model : oldVal
|
||||||
|
}
|
||||||
|
vaeModelField.value = use_vae_model
|
||||||
|
},
|
||||||
|
readUI: () => vaeModelField.value,
|
||||||
|
parse: (val) => val
|
||||||
|
},
|
||||||
|
|
||||||
numOutputsParallel: { name: 'Parallel Images',
|
numOutputsParallel: { name: 'Parallel Images',
|
||||||
setUI: (numOutputsParallel) => {
|
setUI: (numOutputsParallel) => {
|
||||||
@ -310,6 +312,21 @@ function readUI() {
|
|||||||
'reqBody': reqBody
|
'reqBody': reqBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function getModelPath(filename, extensions)
|
||||||
|
{
|
||||||
|
let pathIdx = filename.lastIndexOf('/') // Linux, Mac paths
|
||||||
|
if (pathIdx < 0) {
|
||||||
|
pathIdx = filename.lastIndexOf('\\') // Windows paths.
|
||||||
|
}
|
||||||
|
if (pathIdx >= 0) {
|
||||||
|
filename = filename.slice(pathIdx + 1)
|
||||||
|
}
|
||||||
|
extensions.forEach(ext => {
|
||||||
|
if (filename.endsWith(ext)) {
|
||||||
|
filename = filename.slice(0, filename.length - ext.length)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const TASK_TEXT_MAPPING = {
|
const TASK_TEXT_MAPPING = {
|
||||||
width: 'Width',
|
width: 'Width',
|
||||||
@ -365,29 +382,35 @@ function parseTaskFromText(str) {
|
|||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readFile(file, i) {
|
async function parseContent(text) {
|
||||||
const fileContent = (await file.text()).trim()
|
text = text.trim();
|
||||||
|
if (text.startsWith('{') && text.endsWith('}')) {
|
||||||
// JSON File.
|
|
||||||
if (fileContent.startsWith('{') && fileContent.endsWith('}')) {
|
|
||||||
try {
|
try {
|
||||||
const task = JSON.parse(fileContent)
|
const task = JSON.parse(text)
|
||||||
restoreTaskToUI(task)
|
restoreTaskToUI(task)
|
||||||
|
return true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`file[${i}]:${file.name} - File couldn't be parsed.`, e)
|
console.warn(`JSON text content couldn't be parsed.`, e)
|
||||||
}
|
}
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal txt file.
|
// Normal txt file.
|
||||||
const task = parseTaskFromText(fileContent)
|
const task = parseTaskFromText(text)
|
||||||
if (task) {
|
if (task) {
|
||||||
restoreTaskToUI(task)
|
restoreTaskToUI(task)
|
||||||
|
return true
|
||||||
} else {
|
} else {
|
||||||
console.warn(`file[${i}]:${file.name} - File couldn't be parsed.`)
|
console.warn(`Raw text content couldn't be parsed.`)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function readFile(file, i) {
|
||||||
|
console.log(`Event %o reading file[${i}]:${file.name}...`, e)
|
||||||
|
const fileContent = (await file.text()).trim()
|
||||||
|
return await parseContent(fileContent)
|
||||||
|
}
|
||||||
|
|
||||||
function dropHandler(ev) {
|
function dropHandler(ev) {
|
||||||
console.log('Content dropped...')
|
console.log('Content dropped...')
|
||||||
let items = []
|
let items = []
|
||||||
@ -434,35 +457,52 @@ const TASK_REQ_NO_EXPORT = [
|
|||||||
"use_full_precision",
|
"use_full_precision",
|
||||||
"save_to_disk_path"
|
"save_to_disk_path"
|
||||||
]
|
]
|
||||||
|
const resetSettings = document.getElementById('reset-image-settings')
|
||||||
|
|
||||||
// Retrieve clipboard content and try to parse it
|
function checkReadTextClipboardPermission (result) {
|
||||||
async function pasteFromClipboard() {
|
if (result.state != "granted" && result.state != "prompt") {
|
||||||
//const text = await navigator.clipboard.readText()
|
|
||||||
let text = await navigator.clipboard.readText();
|
|
||||||
text=text.trim();
|
|
||||||
if (text.startsWith('{') && text.endsWith('}')) {
|
|
||||||
try {
|
|
||||||
const task = JSON.parse(text)
|
|
||||||
restoreTaskToUI(task)
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(`Clipboard JSON couldn't be parsed.`, e)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Normal txt file.
|
// PASTE ICON
|
||||||
const task = parseTaskFromText(text)
|
const pasteIcon = document.createElement('i')
|
||||||
if (task) {
|
pasteIcon.className = 'fa-solid fa-paste section-button'
|
||||||
restoreTaskToUI(task)
|
pasteIcon.innerHTML = `<span class="simple-tooltip right">Paste Image Settings</span>`
|
||||||
} else {
|
pasteIcon.addEventListener('click', async (event) => {
|
||||||
console.warn(`Clipboard content - File couldn't be parsed.`)
|
event.stopPropagation()
|
||||||
}
|
// Add css class 'active'
|
||||||
|
pasteIcon.classList.add('active')
|
||||||
|
// In 350 ms remove the 'active' class
|
||||||
|
asyncDelay(350).then(() => pasteIcon.classList.remove('active'))
|
||||||
|
|
||||||
|
// Retrieve clipboard content and try to parse it
|
||||||
|
const text = await navigator.clipboard.readText();
|
||||||
|
await parseContent(text)
|
||||||
|
})
|
||||||
|
resetSettings.parentNode.insertBefore(pasteIcon, resetSettings)
|
||||||
}
|
}
|
||||||
|
navigator.permissions.query({ name: "clipboard-read" }).then(checkReadTextClipboardPermission, (reason) => console.log('clipboard-read is not available. %o', reason))
|
||||||
|
|
||||||
|
document.addEventListener('paste', async (event) => {
|
||||||
|
if (event.target) {
|
||||||
|
const targetTag = event.target.tagName.toLowerCase()
|
||||||
|
// Disable when targeting input elements.
|
||||||
|
if (targetTag === 'input' || targetTag === 'textarea') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const paste = (event.clipboardData || window.clipboardData).getData('text')
|
||||||
|
const selection = window.getSelection()
|
||||||
|
if (selection.toString().trim().length <= 0 && await parseContent(paste)) {
|
||||||
|
event.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Adds a copy and a paste icon if the browser grants permission to write to clipboard.
|
// Adds a copy and a paste icon if the browser grants permission to write to clipboard.
|
||||||
function checkWriteToClipboardPermission (result) {
|
function checkWriteToClipboardPermission (result) {
|
||||||
if (result.state == "granted" || result.state == "prompt") {
|
if (result.state != "granted" && result.state != "prompt") {
|
||||||
const resetSettings = document.getElementById('reset-image-settings')
|
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'
|
||||||
@ -482,23 +522,7 @@ function checkWriteToClipboardPermission (result) {
|
|||||||
navigator.clipboard.writeText(JSON.stringify(uiState, undefined, 4))
|
navigator.clipboard.writeText(JSON.stringify(uiState, undefined, 4))
|
||||||
})
|
})
|
||||||
resetSettings.parentNode.insertBefore(copyIcon, resetSettings)
|
resetSettings.parentNode.insertBefore(copyIcon, resetSettings)
|
||||||
|
|
||||||
// PASTE ICON
|
|
||||||
const pasteIcon = document.createElement('i')
|
|
||||||
pasteIcon.className = 'fa-solid fa-paste section-button'
|
|
||||||
pasteIcon.innerHTML = `<span class="simple-tooltip right">Paste Image Settings</span>`
|
|
||||||
pasteIcon.addEventListener('click', (event) => {
|
|
||||||
event.stopPropagation()
|
|
||||||
// Add css class 'active'
|
|
||||||
pasteIcon.classList.add('active')
|
|
||||||
// In 350 ms remove the 'active' class
|
|
||||||
asyncDelay(350).then(() => pasteIcon.classList.remove('active'))
|
|
||||||
pasteFromClipboard()
|
|
||||||
})
|
|
||||||
resetSettings.parentNode.insertBefore(pasteIcon, resetSettings)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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') {
|
||||||
|
@ -166,11 +166,13 @@ function refreshModifiersState(newTags) {
|
|||||||
const modifierName = modifierCard.querySelector('.modifier-card-label').innerText
|
const modifierName = modifierCard.querySelector('.modifier-card-label').innerText
|
||||||
if (tag == modifierName) {
|
if (tag == 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
|
||||||
activeTags.push({
|
activeTags.push({
|
||||||
'name': modifierName,
|
'name': modifierName,
|
||||||
'element': modifierCard.cloneNode(true),
|
'element': modifierCard.cloneNode(true),
|
||||||
'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
|
||||||
|
91
ui/plugins/ui/Modifiers-dnd.plugin.js
Normal file
91
ui/plugins/ui/Modifiers-dnd.plugin.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
(function () {
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
var styleSheet = document.createElement("style");
|
||||||
|
styleSheet.textContent = `
|
||||||
|
.modifier-card-tiny.drag-sort-active {
|
||||||
|
background: transparent;
|
||||||
|
border: 2px dashed white;
|
||||||
|
opacity:0.2;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(styleSheet);
|
||||||
|
|
||||||
|
// observe for changes in tag list
|
||||||
|
var observer = new MutationObserver(function (mutations) {
|
||||||
|
// mutations.forEach(function (mutation) {
|
||||||
|
if (editorModifierTagsList.childNodes.length > 0) {
|
||||||
|
ModifierDragAndDrop(editorModifierTagsList)
|
||||||
|
}
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
|
||||||
|
observer.observe(editorModifierTagsList, {
|
||||||
|
childList: true
|
||||||
|
})
|
||||||
|
|
||||||
|
let current
|
||||||
|
function ModifierDragAndDrop(target) {
|
||||||
|
let overlays = document.querySelector('#editor-inputs-tags-list').querySelectorAll('.modifier-card-overlay')
|
||||||
|
overlays.forEach (i => {
|
||||||
|
i.parentElement.draggable = true;
|
||||||
|
|
||||||
|
i.parentElement.ondragstart = (e) => {
|
||||||
|
current = i
|
||||||
|
i.parentElement.getElementsByClassName('modifier-card-image-overlay')[0].innerText = ''
|
||||||
|
i.parentElement.draggable = true
|
||||||
|
i.parentElement.classList.add('drag-sort-active')
|
||||||
|
for(let item of document.querySelector('#editor-inputs-tags-list').getElementsByClassName('modifier-card-image-overlay')) {
|
||||||
|
if (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.boxShadow = 'none'
|
||||||
|
}
|
||||||
|
item.innerText = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i.ondragenter = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if (i != current) {
|
||||||
|
let currentPos = 0, droppedPos = 0;
|
||||||
|
for (let it = 0; it < overlays.length; it++) {
|
||||||
|
if (current == overlays[it]) { currentPos = it; }
|
||||||
|
if (i == overlays[it]) { droppedPos = it; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i.parentElement != current.parentElement) {
|
||||||
|
let currentPos = 0, droppedPos = 0
|
||||||
|
for (let it = 0; it < overlays.length; it++) {
|
||||||
|
if (current == overlays[it]) { currentPos = it }
|
||||||
|
if (i == overlays[it]) { droppedPos = it }
|
||||||
|
}
|
||||||
|
if (currentPos < droppedPos) {
|
||||||
|
current = i.parentElement.parentNode.insertBefore(current.parentElement, i.parentElement.nextSibling).getElementsByClassName('modifier-card-overlay')[0]
|
||||||
|
} else {
|
||||||
|
current = i.parentElement.parentNode.insertBefore(current.parentElement, i.parentElement).getElementsByClassName('modifier-card-overlay')[0]
|
||||||
|
}
|
||||||
|
// update activeTags
|
||||||
|
const tag = activeTags.splice(currentPos, 1)
|
||||||
|
activeTags.splice(droppedPos, 0, tag[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
i.ondragover = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
i.parentElement.ondragend = (e) => {
|
||||||
|
i.parentElement.classList.remove('drag-sort-active')
|
||||||
|
for(let item of document.querySelector('#editor-inputs-tags-list').getElementsByClassName('modifier-card-image-overlay')) {
|
||||||
|
item.style.opacity = ''
|
||||||
|
item.innerText = '-'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})()
|
60
ui/plugins/ui/Modifiers-wheel.plugin.js
Normal file
60
ui/plugins/ui/Modifiers-wheel.plugin.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
(function () {
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
// observe for changes in tag list
|
||||||
|
var observer = new MutationObserver(function (mutations) {
|
||||||
|
// mutations.forEach(function (mutation) {
|
||||||
|
if (editorModifierTagsList.childNodes.length > 0) {
|
||||||
|
ModifierMouseWheel(editorModifierTagsList)
|
||||||
|
}
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
|
||||||
|
observer.observe(editorModifierTagsList, {
|
||||||
|
childList: true
|
||||||
|
})
|
||||||
|
|
||||||
|
function ModifierMouseWheel(target) {
|
||||||
|
let overlays = document.querySelector('#editor-inputs-tags-list').querySelectorAll('.modifier-card-overlay')
|
||||||
|
overlays.forEach (i => {
|
||||||
|
i.onwheel = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const delta = Math.sign(event.deltaY)
|
||||||
|
let s = i.parentElement.getElementsByClassName('modifier-card-label')[0].getElementsByTagName("p")[0].innerText
|
||||||
|
if (delta < 0) {
|
||||||
|
// wheel scrolling up
|
||||||
|
if (s.substring(0, 1) == '[' && s.substring(s.length-1) == ']') {
|
||||||
|
s = s.substring(1, s.length - 1)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s.substring(0, 10) !== '('.repeat(10) && s.substring(s.length-10) !== ')'.repeat(10)) {
|
||||||
|
s = '(' + s + ')'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// wheel scrolling down
|
||||||
|
if (s.substring(0, 1) == '(' && s.substring(s.length-1) == ')') {
|
||||||
|
s = s.substring(1, s.length - 1)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s.substring(0, 10) !== '['.repeat(10) && s.substring(s.length-10) !== ']'.repeat(10)) {
|
||||||
|
s = '[' + s + ']'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.parentElement.getElementsByClassName('modifier-card-label')[0].getElementsByTagName("p")[0].innerText = s
|
||||||
|
// update activeTags
|
||||||
|
for (let it = 0; it < overlays.length; it++) {
|
||||||
|
if (i == overlays[it]) {
|
||||||
|
activeTags[it].name = s
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})()
|
Loading…
x
Reference in New Issue
Block a user