Auto-Photoshop-StableDiffus.../index.js

1827 lines
62 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// import {helloHelper} from 'helper.js'
// helloHelper2 = require('./helper.js')
// for organizational proposes
// let g_sdapi_path = 'sdapi'
const g_image_not_found_url =
'https://images.unsplash.com/source-404?fit=crop&fm=jpg&h=800&q=60&w=1200'
const _log = console.log
const _warn = console.warn
const _error = console.error
let g_timer_value = 300 // temporary global variable for testing the timer pause function
let g_version =
'v' +
JSON.parse(require('fs').readFileSync('plugin:manifest.json', 'utf-8'))
.version
let g_sd_url = 'http://127.0.0.1:7860'
let g_online_data_url =
'https://raw.githubusercontent.com/AbdullahAlfaraj/Auto-Photoshop-StableDiffusion-Plugin/master/utility/online_data.json'
const Jimp = require('./jimp/browser/lib/jimp.min')
const Enum = require('./enum')
const helper = require('./helper')
const sdapi = require('./sdapi_py_re')
// const exportHelper = require('./export_png')
const psapi = require('./psapi')
const app = window.require('photoshop').app
const constants = require('photoshop').constants
const { batchPlay } = require('photoshop').action
const { executeAsModal } = require('photoshop').core
// const dialog_box = require('./dialog_box')
// const {entrypoints} = require('uxp')
const { sd_tab_store } = require('./typescripts/dist/bundle')
const html_manip = require('./utility/html_manip')
// const export_png = require('./export_png')
const selection = require('./selection')
const layer_util = require('./utility/layer')
const sd_options = require('./utility/sdapi/options')
// const sd_config = require('./utility/sdapi/config')
const session = require('./utility/session')
const { getSettings } = require('./utility/session')
const script_horde = require('./utility/sd_scripts/horde')
const prompt_shortcut = require('./utility/sdapi/prompt_shortcut')
const formats = require('uxp').storage.formats
const storage = require('uxp').storage
const shell = require('uxp').shell
const fs = storage.localFileSystem
const horde_native = require('./utility/sdapi/horde_native')
const dummy = require('./utility/dummy')
const general = require('./utility/general')
const thumbnail = require('./thumbnail')
const note = require('./utility/notification')
const settings_tab = require('./utility/tab/settings')
//load tabs
const image_search_tab = require('./utility/tab/image_search_tab')
// const share_tab = require('./utility/tab/share_tab')
const api = require('./utility/api')
const {
scripts,
main,
after_detailer_script,
control_net,
logger,
toJS,
viewer,
viewer_util,
preview,
// session_ts,
session_store,
progress,
sd_tab_ts,
// sd_tab_store,
sam,
settings_tab_ts,
one_button_prompt,
enum_ts,
multiPrompts,
ui_ts,
preset,
preset_util,
dialog_box,
sd_tab_util,
node_fs,
io_ts,
extra_page,
selection_ts,
stores,
lexica,
api_ts,
comfyui,
comfyui_util,
comfyui_main_ui,
diffusion_chain,
comfyapi,
} = require('./typescripts/dist/bundle')
const io = require('./utility/io')
function setLogMethod(should_log_to_file = true) {
let timer_id
if (should_log_to_file) {
console.log = (data, ...optional_param) => {
try {
_log(data, ...optional_param)
// const error = new Error({ data, ...optional_param });
const formattedOutput = logger.formateLog(
data,
...optional_param
)
io.IOLog.saveLogToFile({ log: formattedOutput }, 'log.txt')
} catch (e) {
_warn('error while logging: ')
_warn(e)
}
}
console.warn = (data, ...optional_param) => {
try {
_warn(data, ...optional_param)
const error = new Error()
const stackTrace = error.stack
const formattedOutput = logger.formateLog(
data,
...optional_param
)
io.IOLog.saveLogToFile(
{ warning: formattedOutput, stackTrace },
'log.txt'
)
} catch (e) {
_warn('error while logging: ')
_warn(e)
}
}
console.error = (data, ...optional_param) => {
try {
_error(data, ...optional_param)
const error = new Error()
const stackTrace = error.stack
const formattedOutput = logger.formateLog(
data,
...optional_param
)
io.IOLog.saveLogToFile(
{ error: formattedOutput, stackTrace },
'log.txt'
)
} catch (e) {
_error('error while logging: ')
_error(e)
}
}
} else {
console.log = _log
console.warn = _warn
console.error = _error
}
}
setLogMethod(settings_tab_ts.store.data.should_log_to_file)
// const {
// script_args,
// script_name,
// } = require('./ultimate_sd_upscaler/dist/ultimate_sd_upscaler')
let g_horde_generator = new horde_native.hordeGenerator()
let g_automatic_status = Enum.AutomaticStatusEnum['Offline']
let g_current_batch_index = 0
let g_is_laso_inapint_mode = true
//REFACTOR: move to session.js
async function hasSessionSelectionChanged() {
try {
const isSelectionActive = await psapi.checkIfSelectionAreaIsActive()
if (isSelectionActive) {
const current_selection = isSelectionActive // Note: don't use checkIfSelectionAreaIsActive to return the selection object, change this.
if (
await hasSelectionChanged(
current_selection,
g_generation_session.selectionInfo
)
) {
return true
} else {
//selection has not changed
return false
}
}
} catch (e) {
console.warn(e)
return false
}
}
async function calcWidthHeightFromSelection(selectionInfo) {
//set the width and height, hrWidth, and hrHeight using selection info and selection mode
const selection_mode = sd_tab_store.data.selection_mode
if (selection_mode === 'ratio') {
//change (width and height) and (hrWidth, hrHeight) to match the ratio of selection
const base_size = sd_tab_util.helper_store.data.base_size
const [width, height, hr_width, hr_height] =
await selection.selectionToFinalWidthHeight(
selectionInfo,
base_size,
base_size
)
// console.log('width,height: ', width, height)
html_manip.autoFillInWidth(width)
html_manip.autoFillInHeight(height)
html_manip.autoFillInHRWidth(hr_width)
html_manip.autoFillInHRHeight(hr_height)
} else if (selection_mode === 'precise') {
const [width, height, hr_width, hr_height] = [
selectionInfo.width,
selectionInfo.height,
0,
0,
]
html_manip.autoFillInWidth(width)
html_manip.autoFillInHeight(height)
}
}
//REFACTOR: rename to newSelectionEventHandler and move to session.js
const eventHandler = async (event, descriptor) => {
try {
console.log(event, descriptor)
const new_selection_info = await psapi.getSelectionInfoExe()
session_store.updateProperty(
'current_selection_info',
new_selection_info
)
// const isSelectionActive = await psapi.checkIfSelectionAreaIsActive()
if (new_selection_info) {
await calcWidthHeightFromSelection(new_selection_info)
}
} catch (e) {
console.warn(e)
}
}
//REFACTOR: move to generation_settings.js
function getCurrentGenerationModeByValue(value) {
for (let key in generationMode) {
if (
generationMode.hasOwnProperty(key) &&
generationMode[key] === value
) {
return key
}
}
return undefined
}
require('photoshop').action.addNotificationListener(
['set', 'move', 'addTo', 'subtractFrom'],
eventHandler
)
//REFACTOR: move to document.js
async function getUniqueDocumentId() {
console.warn(
'getUniqueDocumentId is deprecated, instead use the methods in IOFolder'
)
try {
let uniqueDocumentId = await psapi.readUniqueDocumentIdExe()
console.log(
'getUniqueDocumentId(): uniqueDocumentId: ',
uniqueDocumentId
)
// Regular expression to check if string is a valid UUID
const regexExp =
/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/gi
// String with valid UUID separated by dash
// const str = 'a24a6ea4-ce75-4665-a070-57453082c256'
const isValidId = regexExp.test(uniqueDocumentId) // true
console.log('isValidId: ', isValidId)
if (isValidId == false) {
let uuid = self.crypto.randomUUID()
console.log(uuid) // for example "36b8f84d-df4e-4d49-b662-bcde71a8764f"
await psapi.saveUniqueDocumentIdExe(uuid)
uniqueDocumentId = uuid
}
return uniqueDocumentId
} catch (e) {
console.warn('warning Document Id may not be valid', e)
}
}
// attach event listeners for tabs
//REFACTOR: move to html_manip.js (?) - if there is no business logic here and it's only for UI.
Array.from(document.querySelectorAll('.sp-tab')).forEach((theTab) => {
theTab.onclick = () => {
try {
// localStorage.setItem("currentTab", theTab.getAttribute("id"));
Array.from(document.querySelectorAll('.sp-tab')).forEach((aTab) => {
if (aTab.getAttribute('id') === theTab.getAttribute('id')) {
aTab.classList.add('selected')
} else {
aTab.classList.remove('selected')
}
})
Array.from(document.querySelectorAll('.sp-tab-page')).forEach(
(tabPage) => {
if (
tabPage
.getAttribute('id')
.startsWith(theTab.getAttribute('id'))
) {
tabPage.classList.add('visible-hack')
} else {
tabPage.classList.remove('visible-hack')
}
}
)
} catch (e) {
console.warn(e)
}
}
})
// entrypoints.setup({
// panels:{
// vanilla: ()=>{
// console.log("you are in the vanilla panel")
// },
// experimental_1: ()=>{
// console.log("you are in the experimental_1 panel")
// }
// }
// }
// )
// just a number that shouldn't unique enough that we will use when save files.
// each session will get a number from 1 to 1000000
//REFACTOR: move to session.js
const random_session_id = Math.floor(Math.random() * 1000000 + 1)
//REFACTOR: move to helpers.js (or other utility file)
function getSelectedText() {
// JavaScript
// // Obtain the object reference for the <textarea>
// const txtarea = document.getElementById("taPrompt");
const promptTextarea = document.querySelector('#taPrompt')
console.log('promptTextarea: ', promptTextarea.value)
// // Obtain the index of the first selected character
var start = promptTextarea.selectionStart
console.log('start: ', start)
// // Obtain the index of the last selected character
// var finish = txtarea.selectionEnd;
// console.log("finish: ",finish)
// // Obtain the selected text
// var sel = txtarea.value.substring(start, finish);
// console.log("selected textarea: ", sel)
// Do something with the selected content
}
//REFACTOR: move to helpers.js
// setInterval(getSelectedText,2000)
function getCommentedString() {
// const text = document.getElementById("taPrompt").value
// let text = `Visit /*W3Schools
// cute, girl, painterly
// *\\ any text
// and prompt`;
// let text = `cute cat /*by greg
// and artgerm
// */ and famous artist`
let text = `Visit /*W3Schools
cute, girl, painterly
*/ any text
and prompt
cute cat /*by greg
and artgerm
*/ and famous artist`
console.log('getCommentedString: text: ', text)
// let pattern = /(\/)(\*)(\s|\S)*\*\\/g;
let pattern = /(\/)(\*)(\s|\S)*?(\*\/)/g
let result = text.match(pattern)
console.log('getCommentedString: ', result)
}
//REFACTOR: move to the notfication.js
async function displayNotification(automatic_status) {
if (automatic_status === Enum.AutomaticStatusEnum['RunningWithApi']) {
//do nothing
} else if (
g_automatic_status === Enum.AutomaticStatusEnum['RunningNoApi']
) {
await note.Notification.webuiAPIMissing()
} else if (g_automatic_status === Enum.AutomaticStatusEnum['Offline']) {
await note.Notification.webuiIsOffline()
}
}
//REFACTOR: move to sdapi.js
async function checkAutoStatus() {
try {
const options = await g_sd_options_obj.getOptions()
if (options) {
//means both automatic1111 and proxy server are online
html_manip.setAutomaticStatus('connected', 'disconnected')
g_automatic_status = Enum.AutomaticStatusEnum['RunningWithApi']
const extension_url = py_re.getExtensionUrl()
const full_url = `${extension_url}/heartbeat`
const heartbeat = (await api.requestGet(full_url))?.heartbeat
if (heartbeat) {
html_manip.setProxyServerStatus('connected', 'disconnected')
session_store.data.auto_photoshop_sd_extension_status = true
} else {
html_manip.setProxyServerStatus('disconnected', 'connected')
g_automatic_status =
Enum.AutomaticStatusEnum['AutoPhotoshopSDExtensionMissing']
session_store.data.auto_photoshop_sd_extension_status = false
}
// html_manip.setProxyServerStatus('connected','disconnected')
} else {
html_manip.setAutomaticStatus('disconnected', 'connected')
if (await sdapi.isWebuiRunning()) {
//running with no api
g_automatic_status = Enum.AutomaticStatusEnum['RunningNoApi']
// await note.Notification.webuiAPIMissing()
} else {
//not running and of course no api
g_automatic_status = Enum.AutomaticStatusEnum['Offline']
// await note.Notification.webuiIsOffline()
}
return g_automatic_status
}
} catch (e) {
console.warn(e)
}
return g_automatic_status
}
//REFACTOR: move to helper.js
function promptShortcutExample() {
let prompt_shortcut_example = {
game_like:
'Unreal Engine, Octane Render, arcane card game ui, hearthstone art style, epic fantasy style art',
large_building_1: 'castle, huge building, large building',
painterly_style_1:
'A full portrait of a beautiful post apocalyptic offworld arctic explorer, intricate, elegant, highly detailed, digital painting, artstation, concept art, smooth, sharp focus, illustration',
ugly: '((((ugly)))), (((duplicate))), ((morbid)), ((mutilated)), out of frame, extra fingers, mutated hands, ((poorly drawn hands)), ((poorly drawn face)), (((mutation))), (((deformed))), ((ugly)), blurry, ((bad anatomy)), (((bad proportions))), ((extra limbs)), cloned face, (((disfigured))), out of frame, ugly, extra limbs, (bad anatomy), gross proportions, (malformed limbs), ((missing arms)), ((missing legs)), (((extra arms))), (((extra legs))), mutated hands, (fused fingers), (too many fingers), (((long neck)))',
}
var JSONInPrettyFormat = JSON.stringify(
prompt_shortcut_example,
undefined,
7
)
document.getElementById('taPromptShortcut').value = JSONInPrettyFormat
return prompt_shortcut_example
}
//**********Start: global variables
let prompt_dir_name = ''
let gImage_paths = []
let g_image_path_to_layer = {}
let g_init_images_dir = './server/python_server/init_images'
//REFACTOR: move to generationSettings.js
gCurrentImagePath = ''
// let g_init_mask_layer;
//REFACTOR: move to generationSettings.js
// let g_mask_related_layers = {}
// let g_init_image_related_layers = {}
//REFACTOR: move to generationSettings.js, Note: numberOfImages deprecated global variable
// let numberOfImages = document.querySelector('#tiNumberOfImages').value
//REFACTOR: move to generationSettings.js
//REFACTOR: move to generationSettings.js
let g_sd_sampler = 'Euler a'
//REFACTOR: move to generationSettings.js
let g_denoising_strength = 0.7
let g_models = []
// let g_models_horde = []
let g_model_title = ''
// let gWidth = 512
// let gHeight = 512
//REFACTOR: move to generationSettings.js
let hWidth = 512
//REFACTOR: move to generationSettings.js
let hHeight = 512
//REFACTOR: move to generationSettings.js
let h_denoising_strength = 0.7
// let g_inpainting_fill = 0
// let g_last_outpaint_layers = []
// let g_last_inpaint_layers = []
// let g_last_snap_and_fill_layers = []
//REFACTOR: move to generationSettings.js
let g_metadatas = []
//REFACTOR: move to generationSettings.js
let g_can_request_progress = true
let g_saved_active_layers = []
let g_saved_active_selection = {}
let g_is_active_layers_stored = false
let g_number_generation_per_session = 0
let g_isViewerMenuDisabled = false // disable the viewer menu and viewerImage when we're importing images into the current document
let g_b_mask_layer_exist = false // true if inpaint mask layer exist, false otherwise.
let g_inpaint_mask_layer
let g_inpaint_mask_layer_history_id //store the history state id when creating a new inpaint mask layer
// let g_selection = {}
//REFACTOR: move to session.js
let g_selection = {}
let g_b_use_smart_object = true // true to keep layer as smart objects, false to rasterize them
let g_sd_options_obj = new sd_options.SdOptions()
let g_controlnet_max_models
let g_generation_session = new session.GenerationSession(0) //session manager
g_generation_session.deactivate() //session starte as inactive
let g_ui_settings_object = ui_ts.getUISettingsObject()
let g_batch_count_interrupt_status = false
const requestState = {
Generate: 'generate',
Interrupt: 'interrupt',
}
let g_request_status = '' //
//REFACTOR: move to Enum.js
const generationMode = {
Txt2Img: 'txt2img',
Img2Img: 'img2img',
Inpaint: 'inpaint',
Outpaint: 'outpaint',
Upscale: 'upscale',
}
const backendTypeEnum = {
Auto1111: 'auto1111',
HordeNative: 'horde_native',
Auto1111HordeExtension: 'auto1111_horde_extension',
}
g_generation_session.mode = generationMode['Txt2Img']
//********** End: global variables */
document
.getElementById('sp-extras-tab')
.addEventListener('click', async (evt) => {
try {
sd_tab_store.updateProperty('mode', 'upscale')
await postModeSelection() // do things after selection
} catch (e) {
console.warn(e)
}
})
//REFACTOR: move to events.js
document
.getElementById('sp-stable-diffusion-ui-tab')
.addEventListener('click', async (evt) => {
try {
sd_tab_store.updateProperty('mode', sd_tab_store.data.rb_mode)
await postModeSelection() // do things after selection
} catch (e) {
console.warn(e)
}
})
//REFACTOR: move to psapi.js
async function createTempInpaintMaskLayer() {
if (!g_b_mask_layer_exist) {
//make new layer "Mask -- Paint White to Mask -- temporary"
const name = 'Mask -- Paint White to Mask -- temporary'
await psapi.unselectActiveLayersExe() // so that the mask layer get create at the top of the layer stocks
const top_layer_doc = await app.activeDocument.layers[0]
g_inpaint_mask_layer = await layer_util.createNewLayerExe(name, 60)
await executeAsModal(async () => {
await g_inpaint_mask_layer.moveAbove(top_layer_doc)
})
// g_inpaint_mask_layer.opacity = 50
g_b_mask_layer_exist = true
const index = app.activeDocument.historyStates.length - 1
g_inpaint_mask_layer_history_id =
app.activeDocument.historyStates[index].id
console.log(
'g_inpaint_mask_layer_history_id: ',
g_inpaint_mask_layer_history_id
)
}
}
//REFACTOR: move to psapi.js
async function deleteTempInpaintMaskLayer() {
console.log(
'g_inpaint_mask_layer_history_id: ',
g_inpaint_mask_layer_history_id
)
const historyBrushTools = app.activeDocument.historyStates
.slice(-10)
.filter(
(h) =>
h.id > g_inpaint_mask_layer_history_id &&
h.name === 'Brush Tool'
)
console.log(historyBrushTools)
if (historyBrushTools.length === 0 && g_b_mask_layer_exist) {
await layer_util.deleteLayers([g_inpaint_mask_layer])
g_b_mask_layer_exist = false
}
}
//REFACTOR: move to ui.js
async function postModeSelection() {
try {
if (sd_tab_store.data.rb_mode === generationMode['Inpaint']) {
//check if the we already have created a mask layer
await createTempInpaintMaskLayer()
} else {
// if we switch from inpaint mode, delete the mask layer
// Find all history states after the creation of the inpaint mask and their name brush tool
await deleteTempInpaintMaskLayer()
}
} catch (e) {
console.warn(e)
}
}
//REFACTOR: move to events.js
document.addEventListener('mouseenter', async (event) => {
try {
//only check if the generation mode has not changed( e.g a session.mode === img2img and the current selection is "img2img" ).
// changing the mode will trigger it's own procedure, so doing it here again is redundant
if (
g_generation_session.isActive() &&
g_generation_session.mode === sd_tab_store.data.rb_mode
) {
//if the generation session is active and the selected mode is still the same as the generation mode
console.log('hover on window')
const new_selection = await psapi.getSelectionInfoExe() //get the current active selection if there is any
if (
new_selection &&
(await hasSelectionChanged(
new_selection,
g_generation_session.selectionInfo
))
) {
// if there is an active selection and if the selection has changed
await calcWidthHeightFromSelection(new_selection)
} else {
// sessionStartHtml(true)//generate more, green color
//if you didn't move the selection.
}
}
} catch (e) {
console.warn(e)
}
})
// function showLayerNames () {
// const app = window.require('photoshop').app
// const allLayers = app.activeDocument.layers
// const allLayerNames = allLayers.map(
// layer => `${layer.name} (${layer.opacity} %)`
// )
// const sortedNames = allLayerNames.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
// document.getElementById('layers').innerHTML = `
// <ul>${sortedNames.map(name => `<li>${name}</li>`).join('')}</ul>`
// }
//REFACTOR: move to psapi.js
function selectTool() {
var doc = app.activeDocument
var activeTool = app.currentTool
// if (activeTool !== toolName) {
// toolName = activeTool;
// doc.activeTool = toolName;
// }
// const util = require('util')
// console.log(util.inspect(myObject, {showHidden: false, depth: null, colors: true}))
console.dir(app, { depth: null })
console.log('hello this is Abdullah')
document.getElementById('layers').innerHTML = `<span>
selectTool was called, ${activeTool}
</span>`
//rectanglemarquee
// await require('photoshop').core.executeAsModal(newNormalLayer);
}
// User picks an image file
// open a explorer for user to select a image file
//REFACTOR: move to psapi.js
async function fillImage() {
const storage = require('uxp').storage
const fs = storage.localFileSystem
let imageFile = await fs.getFileForOpening({
types: storage.fileTypes.images,
})
// Create ImageFill for this image
const ImageFill = require('scenegraph').ImageFill
let fill = new ImageFill(imageFile)
// Set fill of first selected item
selection.items[0].fill = fill
}
// fillImage()
//REFACTOR: move to psapi.js
function pastImage2Layer() {
const { batchPlay } = require('photoshop').action
const { executeAsModal } = require('photoshop').core
executeAsModal(
() => {
// batchPlay([command], {})
const result = batchPlay(
[
{
_obj: 'paste',
antiAlias: {
_enum: 'antiAliasType',
_value: 'antiAliasNone',
},
as: {
_class: 'pixel',
},
_options: {
dialogOptions: 'dontDisplay',
},
},
],
{
synchronousExecution: true,
modalBehavior: 'execute',
}
)
},
{
commandName: 'Create Label',
}
)
}
//REFACTOR: move to ui.js
function sliderToResolution(sliderValue) {
return sliderValue * 64
}
//REFACTOR: move to psapi.js
//store active layers only if they are not stored.
async function storeActiveLayers() {
setTimeout(async () => {
const layers = await app.activeDocument.activeLayers
console.log('storeActiveLayers: ', layers.length)
if (layers.length > 0) {
g_saved_active_layers = layers
await psapi.unselectActiveLayersExe()
}
}, 200)
// if (g_is_active_layers_stored == false) {
// g_saved_active_layers = await app.activeDocument.activeLayers
// g_is_active_layers_stored = true
// await psapi.unselectActiveLayersExe()
// } else {
// }
}
//REFACTOR: move to psapi.js
async function restoreActiveLayers() {
const layers = await app.activeDocument.activeLayers
console.log('restoreActiveLayers: ', layers.length)
if (layers.length == 0) {
await psapi.selectLayersExe(g_saved_active_layers)
g_saved_active_layers = []
}
// if (g_is_active_layers_stored == true) {
// // g_saved_active_layers = await app.activeDocument.activeLayers
// await psapi.selectLayersExe(g_saved_active_layers)
// g_is_active_layers_stored = false
// g_saved_active_layers = []
// }
}
//store active selection only if they are not stored.
//REFACTOR: move to psapi.js
async function storeActiveSelection() {
try {
setTimeout(async () => {
const layers = await app.activeDocument.activeLayers
const current_selection = await psapi.checkIfSelectionAreaIsActive()
console.log('storeActiveSelection: ', current_selection)
if (current_selection) {
g_saved_active_selection = current_selection
await psapi.unSelectMarqueeExe()
}
}, 200)
} catch (e) {
console.warn(e)
}
}
//REFACTOR: move to psapi.js
async function restoreActiveSelection() {
try {
const current_selection = await psapi.checkIfSelectionAreaIsActive()
console.log('restoreActiveSelection: ', current_selection)
if (
!current_selection &&
psapi.isSelectionValid(g_saved_active_selection)
) {
await psapi.reSelectMarqueeExe(g_saved_active_selection)
g_saved_active_selection = {}
}
} catch (e) {
console.warn(e)
}
}
//REFACTOR: unused, remove?
function updateMetadata(new_metadata) {
const metadatas = []
try {
for (metadata of new_metadata) {
metadata_json = JSON.parse(metadata)
console.log('metadata_json:', metadata_json)
metadatas.push(metadata_json)
}
} catch (e) {
console.warn(e)
}
return metadatas
}
//REFACTOR: move to selection.js
async function hasSelectionChanged(new_selection, old_selection) {
if (
new_selection.left === old_selection.left &&
new_selection.bottom === old_selection.bottom &&
new_selection.right === old_selection.right &&
new_selection.top === old_selection.top
) {
return false
} else {
return true
}
}
//REFACTOR: move to ui.js
function updateProgressBarsHtml(new_value) {
document.querySelectorAll('.pProgressBars').forEach((el) => {
// id = el.getAttribute("id")
// console.log("progressbar id:", id)
el.setAttribute('value', new_value)
})
document.querySelectorAll('.lProgressLabel').forEach((el) => {
console.log('updateProgressBarsHtml: ', new_value)
if (new_value > 0) el.innerHTML = 'In progress...'
else el.innerHTML = 'No work in progress'
})
// document.querySelector('#pProgressBar').value
}
//REFACTOR: move to psapi.js
function _base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64)
var len = binary_string.length
var bytes = new Uint8Array(len)
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i)
}
return bytes.buffer
}
//REFACTOR: move to psapi.js
function _arrayBufferToBase64(buffer) {
var binary = ''
var bytes = new Uint8Array(buffer)
var len = bytes.byteLength
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i])
}
return window.btoa(binary)
}
//REFACTOR: move to io.js
async function getDocFolder(doc_uuid) {
try {
// const uuid = await getUniqueDocumentId()
const data_folder = await storage.localFileSystem.getDataFolder()
let doc_folder
try {
doc_folder = await data_folder.getEntry(doc_uuid)
} catch (e) {
console.warn(e)
//create document folder
doc_folder = await data_folder.createFolder(doc_uuid)
}
return doc_folder
} catch (e) {
console.warn(e)
}
}
//REFACTOR: move to document.js
async function getCurrentDocFolder() {
//move to a global utililty lib
const uuid = await getUniqueDocumentId()
let doc_folder = await getDocFolder(uuid)
return doc_folder
}
//REFACTOR: move to document.js
async function getInitImagesDir() {
const uuid = await getUniqueDocumentId()
let doc_folder = await getDocFolder(uuid)
let init_folder
try {
init_folder = await doc_folder.getEntry('init_images')
} catch (e) {
console.warn(e)
//create document folder
init_folder = await doc_folder.createFolder('init_images')
}
return init_folder
}
//REFACTOR: move to document.js
async function base64ToFile(b64Image, image_name = 'output_image.png') {
// const b64Image =
// 'iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81RUkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2Anhf4QtqobAAAAAElFTkSuQmCC'
try {
const img = _base64ToArrayBuffer(b64Image)
const img_name = image_name
const folder = await storage.localFileSystem.getTemporaryFolder()
const file = await folder.createFile(img_name, { overwrite: true })
await file.write(img, { format: storage.formats.binary })
const token = await storage.localFileSystem.createSessionToken(file) // batchPlay requires a token on _path
let place_event_result
let imported_layer
await executeAsModal(async () => {
const result = await batchPlay(
[
{
_obj: 'placeEvent',
// ID: 6,
null: {
_path: token,
_kind: 'local',
},
freeTransformCenterState: {
_enum: 'quadCenterState',
_value: 'QCSAverage',
},
offset: {
_obj: 'offset',
horizontal: {
_unit: 'pixelsUnit',
_value: 0,
},
vertical: {
_unit: 'pixelsUnit',
_value: 0,
},
},
_isCommand: true,
_options: {
dialogOptions: 'dontDisplay',
},
},
],
{
synchronousExecution: true,
modalBehavior: 'execute',
}
)
console.log('placeEmbedd batchPlay result: ', result)
place_event_result = result[0]
imported_layer = await app.activeDocument.activeLayers[0]
})
return imported_layer
} catch (e) {
console.warn(e)
}
// return place_event_result
}
//REFACTOR: move to psapi.js
async function placeImageB64ToLayer(image_path, entery) {
//silent importer
try {
const formats = require('uxp').storage.formats
const storage = require('uxp').storage
const fs = storage.localFileSystem
const names = image_path.split('/')
const length = names.length
const image_name = names[length - 1]
const project_name = names[length - 2]
let pluginFolder = await fs.getPluginFolder()
const image_dir = `./server/python_server/output/${project_name}`
// image_path = "output/f027258e-71b8-430a-9396-0a19425f2b44/output- 1674323725.126322.png"
let img_dir = await pluginFolder.getEntry(image_dir)
// const file = await img_dir.createFile('output- 1674298902.0571606.png', {overwrite: true});
const file = await img_dir.createFile(image_name, { overwrite: true })
const img = await file.read({ format: formats.binary })
const token = await storage.localFileSystem.createSessionToken(file)
let place_event_result
await executeAsModal(async () => {
const result = await batchPlay(
[
{
_obj: 'placeEvent',
ID: 6,
null: {
_path: token,
_kind: 'local',
},
freeTransformCenterState: {
_enum: 'quadCenterState',
_value: 'QCSAverage',
},
offset: {
_obj: 'offset',
horizontal: {
_unit: 'pixelsUnit',
_value: 0,
},
vertical: {
_unit: 'pixelsUnit',
_value: 0,
},
},
_isCommand: true,
_options: {
dialogOptions: 'dontDisplay',
},
},
],
{
synchronousExecution: true,
modalBehavior: 'execute',
}
)
console.log('placeEmbedd batchPlay result: ', result)
place_event_result = result[0]
})
return place_event_result
} catch (e) {
console.warn(e)
}
}
// open an image in the plugin folder as new document
//REFACTOR: move to document.js
async function openImageAction() {
const storage = require('uxp').storage
const fs = storage.localFileSystem
try {
let pluginFolder = await fs.getPluginFolder()
// let theTemplate = await pluginFolder.getEntry("/image1.png");
//directory where all image's request folders are. one folder for each request
const relative_dir_path = `./server/python_server/`
const image_path = `${relative_dir_path}/${gCurrentImagePath}`
// 'C:/Users/abdul/Desktop/photoshop_plugins/my_plugin_1/server/python_server/output- 1670544300.95411.png'
let theTemplate = await pluginFolder.getEntry(image_path)
await app.open(theTemplate)
} catch (e) {
console.warn("couldn't open image ", e)
}
}
//REFACTOR: move to psapi.js
async function openImageExe() {
await require('photoshop').core.executeAsModal(openImageAction)
}
// document.getElementById('btnImagesToLayers').addEventListener('click',openImageExe)
// convert a layer to a smart object
async function convertToSmartObjectAction() {
const batchPlay = require('photoshop').action.batchPlay
const result = await batchPlay(
[
{
_obj: 'newPlacedLayer',
_isCommand: true,
_options: {
dialogOptions: 'dontDisplay',
},
},
],
{}
)
}
//REFACTOR: move to psapi.js
async function convertToSmartObjectExe() {
await require('photoshop').core.executeAsModal(convertToSmartObjectAction)
}
//REFACTOR: unused, remove?
async function silentImagesToLayersExe_old(images_info) {
try {
g_generation_session.isLoadingActive = true
await psapi.reSelectMarqueeExe(g_generation_session.selectionInfo)
image_path_to_layer = {}
console.log(
'silentImagesToLayersExe: images_info.images_paths: ',
images_info.images_paths
)
// Returns a Promise that resolves after "ms" Milliseconds
const timer = (ms) => new Promise((res) => setTimeout(res, ms))
for (image_info of images_info) {
console.log(gCurrentImagePath)
//unselect all layers so that the imported layer get place at the top of the document
await psapi.unselectActiveLayersExe()
let placeEventResult
// if (base64_images) {
// placeEventResult = await base64ToFile(base64_images) //silent import into the document
// } else {
// }
placeEventResult = await base64ToFile(image_info.base64) //silent import into the document
let layer = await app.activeDocument.layers.filter(
(l) => l.id === placeEventResult?.ID
)[0]
// await openImageExe() //local image to new document
// await convertToSmartObjectExe() //convert the current image to smart object
let timer_count = 0
// let layer = await app.activeDocument.activeLayers[0]
console.log('loaded layer: ', layer)
console.log('placeEventResult?.ID: ', placeEventResult?.ID)
while (!layer && timer_count <= 10000) {
await timer(100) // then the created Promise can be awaited
timer_count += 100
// layer = await app.activeDocument.activeLayers[0]
layer = await app.activeDocument.layers.filter(
(l) => l.id === placeEventResult?.ID
)[0]
const active_layer = await app.activeDocument.activeLayers[0]
console.log('active_layer.id: ', active_layer.id)
if (active_layer.id === placeEventResult?.ID) {
layer = active_layer
}
console.log('timer_count: ', timer_count)
console.log('loaded layer: ', layer)
console.log('placeEventResult?.ID: ', placeEventResult?.ID)
}
if (g_b_use_smart_object === false) {
await executeAsModal(async () => {
await layer.rasterize() //rastrize the active layer
})
}
await psapi.selectLayersExe([layer])
await psapi.layerToSelection(g_generation_session.selectionInfo)
// await stackLayers() // move the smart object to the original/old document
// await psapi.layerToSelection(g_generation_session.selectionInfo) //transform the new smart object layer to fit selection area
// layer = await app.activeDocument.activeLayers[0]
await g_generation_session.moveToTopOfOutputGroup(layer)
// const output_group_id = await g_generation_session.outputGroup.id
// let group_index = await psapi.getLayerIndex(output_group_id)
// const indexOffset = 1 //1 for background, 0 if no background exist
// await executeAsModal(async ()=>{
// await psapi.moveToGroupCommand(group_index - indexOffset, layer.id)
// })
image_path_to_layer[image_info.path] = layer
// await reselect(selectionInfo)
}
return image_path_to_layer
} catch (e) {
console.warn(e)
}
g_generation_session.isLoadingActive = false
}
//REFACTOR: move to psapi.js
async function silentImagesToLayersExe(images_info) {
//use active layer instead of placeEventResult
try {
g_generation_session.isLoadingActive = true
await psapi.reSelectMarqueeExe(g_generation_session.selectionInfo) //why do we reselect the session selection area
image_path_to_layer = {}
console.log(
'silentImagesToLayersExe: images_info.images_paths: ',
images_info.images_paths
)
// Returns a Promise that resolves after "ms" Milliseconds
// const timer = (ms) => new Promise((res) => setTimeout(res, ms)) //Todo: move this line to it's own utilit function
for (image_info of images_info) {
console.log(gCurrentImagePath)
//unselect all layers so that the imported layer get place at the top of the document
await psapi.unselectActiveLayersExe()
let imported_layer
// if (base64_images) {
// placeEventResult = await base64ToFile(base64_images) //silent import into the document
// } else {
// }
// imported_layer = await base64ToFile(image_info.base64) //silent import into the document
const selection_info = await g_generation_session.selectionInfo
imported_layer = await io.IO.base64ToLayer(
image_info.base64,
'output_image.png',
selection_info.left,
selection_info.top,
selection_info.width,
selection_info.height
)
if (!layer_util.Layer.doesLayerExist(imported_layer)) {
continue //skip if the import vailed
}
// let layer = await app.activeDocument.layers.filter(
// (l) => l.id === placeEventResult?.ID
// )[0]
let timer_count = 0
// let layer = await app.activeDocument.activeLayers[0]
console.log('imported_layer: ', imported_layer)
// while (timer_count <= 10000) {
// await timer(100) // then the created Promise can be awaited
// timer_count += 100
// // layer = await app.activeDocument.activeLayers[0]
// console.log('timer_count: ', timer_count)
// console.log('loaded layer: ', imported_layer)
// }
if (g_b_use_smart_object === false) {
await executeAsModal(async () => {
await imported_layer.rasterize() //rastrize the active layer
})
}
// await psapi.selectLayersExe([imported_layer])
// await psapi.layerToSelection(g_generation_session.selectionInfo)// not needed
await g_generation_session.moveToTopOfOutputGroup(imported_layer)
await psapi.setVisibleExe(imported_layer, false) // turn off the visibility for the layer
image_path_to_layer[image_info.path] = imported_layer
// await reselect(selectionInfo)
}
return image_path_to_layer
} catch (e) {
console.warn(e)
}
g_generation_session.isLoadingActive = false
}
//stack layer to original document
//REFACTOR: move to psapi.js
async function stackLayers() {
//workingDoc is the project you are using stable diffusion in
const workingDoc = app.documents[0]
//you should not open two multiple projects this script assume there is only one project opened
const docsToStack = app.documents.filter(
(doc) => doc._id !== workingDoc._id
)
let docCounter = 0
// execute as modal is required for functions that change the state of Photoshop or documents
// think of it as a function that 'wraps' yours and tells Photoshop to go into a modal state and not allow anything to interrupt it from doing whatever is contained in the executeAsModal
// we also call it with the await keyword to tell JS that we want to wait for it to complete before moving on to later code (in this case there isn't any though)
await require('photoshop').core.executeAsModal(async () => {
// increment counter
docCounter++
// loop through other open docs
for (const doc of docsToStack) {
// flatten
// doc.flatten();
// rename layer with counter
doc.layers[0].name = `Layer ${docCounter}`
// increment counter
docCounter++
// duplicate layer to docZero
doc.layers[0].duplicate(workingDoc)
// close doc
await doc.closeWithoutSaving()
}
})
}
function removeInitImageFromViewer() {}
function removeMaskFromViewer() {}
//REFACTOR: move to viewer.js
async function viewerThumbnailclickHandler(e, viewer_obj_owner) {
if (g_isViewerMenuDisabled) {
return g_isViewerMenuDisabled
}
let click_type = Enum.clickTypeEnum['Click']
if (e.shiftKey) {
click_type = Enum.clickTypeEnum['ShiftClick']
} else if (e.altKey) {
click_type = Enum.clickTypeEnum['AltClick']
}
if (
viewer_obj_owner.isActive() &&
click_type === Enum.clickTypeEnum['Click']
) {
//convert consecutive clicks to AltClick
click_type = Enum.clickTypeEnum['SecondClick']
console.log('converted click_type: ', click_type)
}
await executeAsModal(async () => {
//get type of click
await viewer_obj_owner.click(click_type)
})
}
// async function NewViewerImageClickHandler(img, viewer_obj_owner) {
// try {
// } catch (e) {
// console.warn(e)
// }
// }
//REFACTOR: move to viewer.js
function createViewerImgHtml(output_dir_relative, image_path, base64_image) {
const img = document.createElement('img')
// img.src = `${output_dir_relative}/${image_path}`
img.src = base64ToSrc(base64_image)
img.className = 'viewer-image'
console.log('image_path: ', image_path)
// img.dataset.image_id = layer_id
// img.dataset.image_path = image_path // image_path is not the same as src
return img
}
//REFACTOR: move to psapi.js
function toggleLayerVisibility(layer, b_on) {
try {
layer.visible = b_on
} catch (e) {
console.warn(e)
}
}
//REFACTOR: move to psapi.js
async function turnMaskVisible(
b_mask_group_on,
b_white_mask_on,
b_solid_black_mask_on
) {
//will turn a mask group, white layer mask, and the solid black layer on and off
try {
await executeAsModal(() => {
toggleLayerVisibility(g_mask_group, b_mask_group_on)
toggleLayerVisibility(g_white_mask, b_white_mask_on)
toggleLayerVisibility(g_solid_black_mask, b_solid_black_mask_on)
})
} catch (e) {
console.warn(e)
}
}
//REFACTOR: move to events.js
document
.getElementById('btnLoadPromptShortcut')
.addEventListener('click', async function () {
await sd_tab_util.loadPromptShortcut()
})
//REFACTOR: move to events.js
document
.getElementById('btnUpdatePromptShortcut')
.addEventListener('click', async function () {
try {
// prompt_shortcut = await sdapi.loadPromptShortcut()
const prompt_shortcut_string =
document.getElementById('taPromptShortcut').value
const prompt_shortcut = JSON.parse(prompt_shortcut_string)
var newKey = document.getElementById('KeyPromptShortcut').value
var newValue = document.getElementById('ValuePromptShortcut').value
console.log(newKey)
console.log(newValue)
prompt_shortcut[newKey] = newValue
var JSONInPrettyFormat = JSON.stringify(
prompt_shortcut,
undefined,
4
)
console.log(JSONInPrettyFormat)
document.getElementById('taPromptShortcut').value =
JSONInPrettyFormat
await refreshPromptMenu()
} catch (e) {
console.warn(`loadPromptShortcut warning: ${e}`)
}
})
//REFACTOR: move to events.js
document
.getElementById('btnSavePromptShortcut')
.addEventListener('click', async function () {
try {
const r1 = await dialog_box.prompt(
'Are you sure you want to save prompt shortcut?',
"This will override your old prompt shortcut file, you can't undo this operation",
['Cancel', 'Save']
)
if ((r1 || 'Save') !== 'Save') {
/* cancelled or No */
console.log('cancel')
} else {
/* Yes */
console.log('Save')
prompt_shortcut_string =
document.getElementById('taPromptShortcut').value
let prompt_shortcut = JSON.parse(prompt_shortcut_string)
prompt_shortcut = await sdapi.savePromptShortcut(
prompt_shortcut
)
// var JSONInPrettyFormat = JSON.stringify(prompt_shortcut, undefined, 4);
console.log('prompt_shortcut was saved: ', prompt_shortcut)
}
} catch (e) {
console.warn(`savePromptShortcut warning: ${e}`)
}
})
//REFACTOR: move to document.js
async function loadPromptShortcut() {
let prompt_shortcut = await sdapi.loadPromptShortcut() // always return json object or empty object {}
if (!Object.keys(prompt_shortcut).length) {
//load the default prompt shortcut if we have an empty prompt shortcut object (on failure to load from file)
prompt_shortcut = promptShortcutExample()
}
html_manip.setPromptShortcut(prompt_shortcut) // fill the prompt shortcut textarea
await refreshPromptMenu() //refresh the prompt menu
}
//REFACTOR: move to ui.js
async function refreshPromptMenu() {
try {
//get the prompt_shortcut_json
//iterate over the each entery
const prompt_shortcut = html_manip.getPromptShortcut()
const prompt_shortcut_menu = document.getElementById(
'mPromptShortcutMenu'
)
prompt_shortcut_menu.innerHTML = ''
for (const [key, value] of Object.entries(prompt_shortcut)) {
if (value.trim() === '') {
//skip empty spaces
continue
}
const menu_item_element = document.createElement('sp-menu-item')
menu_item_element.innerHTML = key
prompt_shortcut_menu.appendChild(menu_item_element)
}
} catch (e) {
console.warn(e)
}
}
//REFACTOR: move to events.js
document
.getElementById('mPromptShortcutMenu')
.addEventListener('change', (evt) => {
const prompt_shortcut = html_manip.getPromptShortcut()
const key = evt.target.value
console.log('key:', key)
changePromptShortcutKey(key)
changePromptShortcutValue(prompt_shortcut[key])
})
//REFACTOR: move to events.js
document
.getElementById('btnRefreshPromptShortcutMenu')
.addEventListener('click', async () => {
await refreshPromptMenu()
})
//REFACTOR: move to ui.js
function changePromptShortcutKey(new_key) {
document.getElementById('KeyPromptShortcut').value = new_key
}
//REFACTOR: move to ui.js
function changePromptShortcutValue(new_value) {
document.getElementById('ValuePromptShortcut').value = new_value
}
//REFACTOR: move to document.js
async function downloadIt(link, writeable_entry, image_file_name) {
const image = await fetch(link)
console.log(link)
const storage = require('uxp').storage
const fs = storage.localFileSystem
try {
const img = await image.arrayBuffer()
// const file = await fs.getFileForSaving("image.png");
// const folder = await storage.localFileSystem.getTemporaryFolder()
const file = await writeable_entry.createFile(image_file_name, {
overwrite: true,
})
// const file = await fs.getTempFolder()
await file.write(img)
const currentDocument = app.activeDocument
let newDocument
let new_layer
try {
newDocument = await app.open(file)
if (currentDocument) {
new_layer = await newDocument.activeLayers[0].duplicate(
currentDocument
)
await newDocument.closeWithoutSaving()
}
} catch (e) {
console.warn(e)
}
if (!file) {
return
}
return new_layer
} catch (e) {
console.warn(e)
}
}
//REFACTOR: move to document.js
async function downloadItExe(link, writeable_entry, image_file_name) {
let new_layer
await executeAsModal(async () => {
try {
new_layer = await downloadIt(link, writeable_entry, image_file_name)
} catch (e) {
console.warn(e)
}
})
return new_layer
}
//REFACTOR: move to psapi.js
function base64ToSrc(base64_image) {
const image_src = `data:image/png;base64, ${base64_image}`
return image_src
}
const py_re = require('./utility/sdapi/python_replacement')
function getDimensions(image) {
return new Promise((resolve, reject) => {
var img = new Image()
img.src = image
img.addEventListener('load', function () {
// image.width × image.height
console.log('image loaded:', img.width, img.height)
resolve({ width: img.width, height: img.height })
})
// img.onload = () => {
// resolve({ width: img.width, height: img.height })
// }
})
}
//REFACTOR: move to events.js
function switchMenu(rb) {
const tab_button_name = rb.dataset['tab-name']
const tab_page_name = `${tab_button_name}-page`
try {
const contianer_class = rb.parentElement.dataset['container-class']
const radio_group = rb.parentElement
document
.getElementById(tab_button_name)
.addEventListener('click', () => {
document.getElementById(tab_button_name)
const option_container = document
.getElementById(tab_page_name)
.querySelector(`.${contianer_class}`)
// .querySelector('.subTabOptionsContainer')
rb.checked = true
option_container.appendChild(radio_group)
})
rb.onclick = () => {
document.getElementById(tab_button_name).click()
}
} catch (e) {
console.warn(e)
}
}
//REFACTOR: move to events.js
document
.getElementById('btnSaveHordeSettings')
.addEventListener('click', async () => {
await horde_native.HordeSettings.saveSettings()
})
document
.getElementById('btnSaveHordeSettings')
.addEventListener('click', async () => {
await horde_native.HordeSettings.saveSettings()
})
let g_viewer_sub_menu_list = []
const submenu = {
viewer: {
value: 'viewer',
Label: 'Viewer',
'data-tab-name': 'sp-viewer-tab',
},
prompts_library: {
value: 'prompts-library',
Label: 'Prompts Library',
'data-tab-name': 'sp-prompts-library-tab',
},
image_search: {
value: 'image_search',
Label: 'Image Search',
'data-tab-name': 'sp-image_search-tab',
},
}
{
/* <li>
<input
type="radio"
id="option1"
name="options"
checked
class="sub-menu-tab-class"
/>
<label for="option1">Option 1</label>
</li> */
}
const viewer_sub_menu_html = document.getElementById('viewer-sub-menu')
// const li_elm = document.createElement('li')
// const input_option = document.createElement('input')
// const label_option = document.createElement('label')
// li_elm.appendChild(input_option)
// li_elm.appendChild(label_option)
// viewer_sub_menu_html.appendChild(li_elm)
for (const [option_id, option_data] of Object.entries(submenu)) {
const li = document.createElement('li')
li.innerHTML = `
<input
type="radio"
id="${option_id}"
name="options"
checked
class="sub-menu-tab-class"
data-tab-name='${option_data['data-tab-name']}'
/>
<label for="${option_id}">${option_data.Label}</label>
`
viewer_sub_menu_html.appendChild(li)
}
function switchMenu_new(rb) {
const tab_button_name = rb.dataset['tab-name']
const tab_page_name = `${tab_button_name}-page`
try {
const contianer_class =
rb.parentElement.parentElement.parentElement.dataset[
'container-class'
] //input_option.li.ul.div.dataset['container-class']
const radio_group = rb.parentElement.parentElement.parentElement //radio_group in this case is ul
document
.getElementById(tab_button_name)
.addEventListener('click', () => {
document.getElementById(tab_button_name)
const option_container = document
.getElementById(tab_page_name)
.querySelector(`.${contianer_class}`)
// .querySelector('.subTabOptionsContainer')
rb.checked = true
option_container.appendChild(radio_group)
})
rb.parentElement.onclick = () => {
document.getElementById(tab_button_name).click()
}
} catch (e) {
console.warn(e)
}
}
document.getElementsByClassName('sub-menu-tab-class').forEach((element) => {
switchMenu_new(element)
// const li_element = element.parentElement
// li_element.addEventListener('click', (event) => {
// //doesn't get trigger don't know why
// console.log('sub menu clicked')
// element.checked = true
// })
// switchMenu_new(element)
})
Array.from(document.querySelectorAll('.rbSubTab')).forEach((rb) => {
switchMenu(rb)
})
// document.getElementById('scrollToViewer').addEventListener('click', () => {
// document
// .querySelector('#sp-stable-diffusion-ui-tab-page .reactViewerContainer')
// .scrollIntoView()
// })
const photoshop = require('photoshop')
const uxp = require('uxp')
async function openFileFromUrl(url, format = 'gif') {
try {
// Download the image
const response = await fetch(url)
// const blob = await response.blob()
// Convert the blob to an ArrayBuffer
const arrayBuffer = await response.arrayBuffer()
// Save the image to a temporary file
const tempFolder = await storage.localFileSystem.getTemporaryFolder()
const randomNumber = Math.floor(Math.random() * 1000000000) // generates a random number between 0 and 999999999
const tempFile = await tempFolder.createFile(
`temp_${randomNumber}.${format}`,
{
overwrite: true,
}
)
await tempFile.write(arrayBuffer)
// Open the file in Photoshop
const document = await app.open(tempFile)
console.log(`Opened document: ${document.title}`)
} catch (e) {
console.error(e)
}
}
async function openFileFromUrlExe(url, format = 'gif') {
await executeAsModal(async () => {
await openFileFromUrl(url, format)
})
}
let comfy_server = new diffusion_chain.ComfyServer('http://127.0.0.1:8188')
let comfy_object_info = diffusion_chain.ComfyApi.objectInfo(comfy_server)