diff --git a/i18n/zh_CN/ps-plugin.json b/i18n/zh_CN/ps-plugin.json index 5cb2e7c7..ae0676b5 100644 --- a/i18n/zh_CN/ps-plugin.json +++ b/i18n/zh_CN/ps-plugin.json @@ -11,7 +11,11 @@ "Select Lora": "选择 Lora", "use lora in your prompt": "在提示中使用 lora", "Generate": "生成", - "Generate txt2img": "生成 txt2img", + "Generate Txt2Img": "生成 Txt2Img", + "Generate Img2Img": "生成 Img2Img", + "Generate Inpaint": "生成 Inpaint", + "Generate Outpaint": "生成 Outpaint", + "outpaint": "outpaint", "Progress...": "进度...", "Toggle the visibility of the Preview Image on the canvas": "切换画布上预览图像的可见性", "Move and reSize the highlighted layer to fit into the Selection Area": "移动和调整突出显示的图层以适合选择区域", @@ -67,6 +71,7 @@ "Random": "随机", "Last": "最后", "Show Samplers": "显示采样器", + "Sampling Steps:": "Sampling Steps:", "Select A Script": "选择脚本", "Activate": "激活", "Viewer": "查看器", @@ -181,4 +186,4 @@ "Delete all generated images from the canvas": "在画布上删除所有生成图像", "Keep only the highlighted images": "在画布上保留选中的图像", "Generate More": "生成更多" - } \ No newline at end of file +} diff --git a/index.html b/index.html index 2f6f1d11..29c78c3f 100644 --- a/index.html +++ b/index.html @@ -344,7 +344,7 @@ /* display: none; */ flex: 1 1 auto; /* overflow: scroll; */ - overflow-y: scroll; + /* overflow-y: scroll; */ padding: 12px 0; flex-direction: column; } @@ -721,6 +721,43 @@ } + + + @@ -1161,6 +1198,9 @@
+
+ + @@ -1182,6 +1222,7 @@
+ diff --git a/index.js b/index.js index b5beface..f05f0803 100644 --- a/index.js +++ b/index.js @@ -70,6 +70,7 @@ const { logger, toJS, viewer, + viewer_util, preview, // session_ts, session_store, @@ -93,6 +94,11 @@ const { stores, lexica, api_ts, + comfyui, + comfyui_util, + comfyui_main_ui, + + comfyapi, } = require('./typescripts/dist/bundle') const io = require('./utility/io') @@ -532,8 +538,6 @@ 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() -g_sd_options_obj.getOptions() - let g_controlnet_max_models let g_generation_session = new session.GenerationSession(0) //session manager @@ -1795,9 +1799,13 @@ async function openFileFromUrl(url, format = 'gif') { // Save the image to a temporary file const tempFolder = await storage.localFileSystem.getTemporaryFolder() - const tempFile = await tempFolder.createFile(`temp.${format}`, { - overwrite: true, - }) + 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 diff --git a/package-lock.json b/package-lock.json index a04a78f0..38adca78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9272,15 +9272,15 @@ } }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/undefsafe": { @@ -16652,9 +16652,9 @@ } }, "typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==" + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==" }, "undefsafe": { "version": "2.0.5", diff --git a/package.json b/package.json index 356303a8..8c4ecdcb 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@babel/plugin-transform-react-jsx": "^7.21.5", "@svgr/webpack": "^8.0.1", "babel-loader": "^9.1.2", + "chalk": "^5.3.0", "clean-webpack-plugin": "^4.0.0", "commander": "^11.0.0", "copy-webpack-plugin": "^11.0.0", @@ -45,7 +46,6 @@ "url-loader": "^4.1.1", "webpack": "^5.82.1", "webpack-cli": "^5.1.1", - "chalk": "^5.3.0", "yazl": "^2.5.1" }, "scripts": { @@ -64,4 +64,4 @@ "url": "https://github.com/AbdullahAlfaraj/Auto-Photoshop-StableDiffusion-Plugin/issues" }, "homepage": "https://github.com/AbdullahAlfaraj/Auto-Photoshop-StableDiffusion-Plugin#readme" -} \ No newline at end of file +} diff --git a/presets/img2img.json b/presets/img2img.json index e17f70a1..6e74f9d9 100644 --- a/presets/img2img.json +++ b/presets/img2img.json @@ -6,8 +6,7 @@ "batch_size": 1, "batch_count": 1, "steps": 20, - "width": 512, - "height": 512, + "ratio": 1, "cfg": 7, "b_width_height_link": true, @@ -28,13 +27,5 @@ "hr_upscaler": "", "selection_mode": "ratio" }, - "controlnet_tab_preset": [ - {}, - {}, - {}, - {}, - {}, - {}, - {} - ] -} \ No newline at end of file + "controlnet_tab_preset": [{}, {}, {}, {}, {}, {}, {}] +} diff --git a/presets/inpaint.json b/presets/inpaint.json index 7da37df5..6d887c9f 100644 --- a/presets/inpaint.json +++ b/presets/inpaint.json @@ -6,8 +6,6 @@ "batch_size": 1, "batch_count": 1, "steps": 20, - "width": 512, - "height": 512, "ratio": 1, "cfg": 7, "b_width_height_link": true, @@ -28,13 +26,5 @@ "hr_upscaler": "", "selection_mode": "ratio" }, - "controlnet_tab_preset": [ - {}, - {}, - {}, - {}, - {}, - {}, - {} - ] -} \ No newline at end of file + "controlnet_tab_preset": [{}, {}, {}, {}, {}, {}, {}] +} diff --git a/sdapi_py_re.js b/sdapi_py_re.js index 605c5528..421f2d70 100644 --- a/sdapi_py_re.js +++ b/sdapi_py_re.js @@ -3,9 +3,6 @@ const { base64ToBase64Url } = require('./utility/general') const py_re = require('./utility/sdapi/python_replacement') const Enum = require('./enum') -const { control_net } = require('./typescripts/dist/bundle') -const { mapPluginSettingsToControlNet, getEnableControlNet, getModuleDetail } = - control_net const api = require('./utility/api') //javascript plugin can't read images from local directory so we send a request to local server to read the image file and send it back to plugin as image string base64 @@ -359,186 +356,6 @@ async function requestExtraSingleImage(payload) { } } -//REFACTOR: reuse the same code for (requestControlNetTxt2Img,requestControlNetImg2Img) -async function requestControlNetTxt2Img(plugin_settings) { - console.log('requestControlNetTxt2Img: ') - - // const full_url = `${g_sd_url}/controlnet/txt2img` - const full_url = `${g_sd_url}/sdapi/v1/txt2img` - - const control_net_settings = mapPluginSettingsToControlNet(plugin_settings) - let control_networks = [] - // let active_control_networks = 0 - for (let index = 0; index < g_controlnet_max_models; index++) { - if (!getEnableControlNet(index)) { - control_networks[index] = false - continue - } - control_networks[index] = true - - if (!control_net_settings['controlnet_units'][index]['input_image']) { - app.showAlert('you need to add a valid ControlNet input image') - throw 'you need to add a valid ControlNet input image' - } - - if (!control_net_settings['controlnet_units'][index]['module']) { - app.showAlert('you need to select a valid ControlNet Module') - throw 'you need to select a valid ControlNet Module' - } - - if ( - (!control_net_settings['controlnet_units'][index]['model'] && - !getModuleDetail()[ - control_net_settings['controlnet_units'][index]['module'] - ].model_free) || - control_net_settings['controlnet_units'][index]['model'] === 'none' - ) { - app.showAlert('you need to select a valid ControlNet Model') - throw 'you need to select a valid ControlNet Model' - } - // active_control_networks++ - } - - let request = await fetch(full_url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(control_net_settings), - }) - - let json = await request.json() - console.log('json:', json) - - //update the mask in controlNet tab - const numOfImages = json['images'].length - let numberOfAnnotations = - numOfImages - g_generation_session.last_settings.batch_size - if (numberOfAnnotations < 0) numberOfAnnotations = 0 - - const base64_mask = json['images'].slice(numOfImages - numberOfAnnotations) - - let mask_index = 0 - - for (let index = 0; index < control_networks.length; index++) { - if ( - control_networks[index] == false || - mask_index >= numberOfAnnotations - ) - continue - control_net.setControlDetectMapSrc(base64_mask[mask_index], index) - g_generation_session.controlNetMask[index] = base64_mask[mask_index] - mask_index++ - } - // g_generation_session.controlNetMask = base64_mask - - const standard_response = await py_re.convertToStandardResponse( - control_net_settings, - json['images'].slice(0, numOfImages - numberOfAnnotations), - plugin_settings['uniqueDocumentId'] - ) - console.log('standard_response:', standard_response) - - return standard_response -} - -//REFACTOR: reuse the same code for (requestControlNetTxt2Img,requestControlNetImg2Img) -async function requestControlNetImg2Img(plugin_settings) { - console.log('requestControlNetImg2Img: ') - - const full_url = `${g_sd_url}/sdapi/v1/img2img` - const control_net_settings = mapPluginSettingsToControlNet(plugin_settings) - - // let control_networks = 0 - let control_networks = [] - for (let index = 0; index < g_controlnet_max_models; index++) { - if (!getEnableControlNet(index)) { - control_networks[index] = false - continue - } - control_networks[index] = true - if (!control_net_settings['controlnet_units'][index]['input_image']) { - app.showAlert('you need to add a valid ControlNet input image') - throw 'you need to add a valid ControlNet input image' - } - - if (!control_net_settings['controlnet_units'][index]['module']) { - app.showAlert('you need to select a valid ControlNet Module') - throw 'you need to select a valid ControlNet Module' - } - if ( - (!control_net_settings['controlnet_units'][index]['model'] && - !getModuleDetail()[ - control_net_settings['controlnet_units'][index]['module'] - ].model_free) || - control_net_settings['controlnet_units'][index]['model'] === 'none' - ) { - app.showAlert('you need to select a valid ControlNet Model') - throw 'you need to select a valid ControlNet Model' - } - } - - let request = await fetch(full_url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(control_net_settings), - // body: JSON.stringify(payload), - }) - - let json = await request.json() - console.log('json:', json) - - //update the mask in controlNet tab - const numOfImages = json['images'].length - let numberOfAnnotations = - numOfImages - g_generation_session.last_settings.batch_size - if (numberOfAnnotations < 0) numberOfAnnotations = 0 - - // To fix a bug: when Ultimate SD Upscale is active and running, the detection maps won’t be retrieved. - // So set its value to 0 to avoid the result images being loaded in the annotation map interface. - if ( - scripts.script_store.isInstalled() && - scripts.script_store.is_active && - scripts.script_store.selected_script_name !== 'None' && - scripts.script_store.is_selected_script_available - ) { - numberOfAnnotations = 0 - } - const base64_mask = json['images'].slice(numOfImages - numberOfAnnotations) - - let mask_index = 0 - for (let index = 0; index < control_networks.length; index++) { - if ( - control_networks[index] == false || - mask_index >= numberOfAnnotations - ) - continue - control_net.setControlDetectMapSrc(base64_mask[mask_index], index) - g_generation_session.controlNetMask[index] = base64_mask[mask_index] - mask_index++ - } - - // g_generation_session.controlNetMask = base64_mask - - const standard_response = await py_re.convertToStandardResponse( - control_net_settings, - json['images'].slice(0, numOfImages - numberOfAnnotations), - plugin_settings['uniqueDocumentId'] - ) - console.log('standard_response:', standard_response) - - // //get all images except last because it's the mask - // for (const image of json['images'].slice(0, -1)) { - // await io.IO.base64ToLayer(image) - // } - - return standard_response -} - async function isWebuiRunning() { console.log('isWebuiRunning: ') let json = [] @@ -577,7 +394,5 @@ module.exports = { // requestHordeStatus, requestExtraSingleImage, - requestControlNetTxt2Img, - requestControlNetImg2Img, isWebuiRunning, } diff --git a/selection.js b/selection.js index 412b606c..98782bcd 100644 --- a/selection.js +++ b/selection.js @@ -324,6 +324,24 @@ async function channelToSelectionExe(channel_name = 'mask') { } } +function keepRatio(selectionInfo, offset) { + // Calculate the current width and height + let width = selectionInfo.right - selectionInfo.left + let height = selectionInfo.bottom - selectionInfo.top + + // Calculate the new coordinates with the offset + selectionInfo.right += offset + selectionInfo.left -= offset + selectionInfo.bottom += offset + selectionInfo.top -= offset + + // Update width and height + selectionInfo.width = width + 2 * offset + selectionInfo.height = height + 2 * offset + + return selectionInfo +} + function makeSquare(selectionInfo, offset) { // Calculate the current width and height let width = selectionInfo.right - selectionInfo.left @@ -349,12 +367,20 @@ function makeSquare(selectionInfo, offset) { return selectionInfo } -async function inpaintLassoInitImageAndMask(channel_name = 'mask', offset = 0) { +async function inpaintLassoInitImageAndMask( + channel_name = 'mask', + offset = 0, + make_square = true +) { const selectionInfo = await psapi.getSelectionInfoExe() //convert the selection box into square box so that you have best output results - const squareSelection = makeSquare(selectionInfo, offset) + + const newSelection = make_square + ? makeSquare(selectionInfo, offset) + : keepRatio(selectionInfo, offset) + //correct width and height sliders, since this is lasso mode. - await calcWidthHeightFromSelection(squareSelection) + await calcWidthHeightFromSelection(newSelection) async function getImageFromCanvas() { const width = html_manip.getWidth() @@ -363,7 +389,7 @@ async function inpaintLassoInitImageAndMask(channel_name = 'mask', offset = 0) { const base64 = await io.IO.getSelectionFromCanvasAsBase64Interface_New( width, height, - squareSelection, + newSelection, true ) return base64 @@ -396,7 +422,7 @@ async function inpaintLassoInitImageAndMask(channel_name = 'mask', offset = 0) { synchronousExecution: true, }) // const selection_info = await psapi.getSelectionInfoExe() - mask_base64 = await fillSelectionWhiteOutsideBlack(squareSelection) + mask_base64 = await fillSelectionWhiteOutsideBlack(newSelection) }) //save laso selection to channel diff --git a/typescripts/after_detailer/after_detailer.tsx b/typescripts/after_detailer/after_detailer.tsx index 88768516..e734f7df 100644 --- a/typescripts/after_detailer/after_detailer.tsx +++ b/typescripts/after_detailer/after_detailer.tsx @@ -21,6 +21,7 @@ import { ErrorBoundary } from '../util/errorBoundary' import { ScriptMode } from '../util/ts/enum' import './style/after_detailer.css' +import Locale from '../locale/locale' declare let g_sd_url: string @@ -133,8 +134,7 @@ export class AfterDetailerComponent extends React.Component<{ Automatic1111 webui @@ -150,12 +150,11 @@ export class AfterDetailerComponent extends React.Component<{ store.updateProperty('is_enabled', event.target.checked) }} > - {'Activate'} + {Locale('Activate')} = {} + comfy_url: string + status: boolean = false + + constructor(comfy_url: string) { + this.comfy_url = comfy_url + } + async init() { + try { + this.object_info = await this.initializeObjectInfo(this.comfy_url) + this.status = true + return this.object_info + } catch (e) { + console.error(e) + app.showAlert(`${e}`) + this.status = false + } + } + setUrl(comfy_url: string) { + this.comfy_url = comfy_url + } + async refresh() { + this.object_info = await this.initializeObjectInfo(this.comfy_url) + } + async queue() { + const res = await requestGet(`${this.comfy_url}/queue`) + const queue_running = res?.queue_running ? res?.queue_running : [] + const queue_pending = res?.queue_pending ? res?.queue_pending : [] + return { queue_running, queue_pending } + } + async prompt(prompt: any) { + try { + const payload = { + prompt: prompt, + } + const res = await requestPost(`${this.comfy_url}/prompt`, payload) + + return res + } catch (e) { + console.error(e) + } + } + + async getHistory(prompt_id: string = '') { + try { + const url = `http://127.0.0.1:8188/history/${prompt_id}` + const res = await requestGet(`${this.comfy_url}/history`) + return res + } catch (e) { + console.error(e) + } + } + async view( + filename: string, + type: string = 'output', + subfolder: string = '' + ): Promise { + const ab: ArrayBuffer = await requestGet( + `${this.comfy_url}/view?subfolder=${subfolder}&type=${type}&filename=${filename}` + ) + return Buffer.from(ab).toString('base64') + } + + async initializeObjectInfo(comfy_url: string) { + try { + const full_url = `${comfy_url}/object_info` + const object_info = await requestGet(full_url) + if (!object_info) + throw `can not request from comfyui url: ${comfy_url}` + return object_info + } catch (e) { + console.error(e) + throw e + } + } + getObjectInfo() { + try { + return this.object_info + } catch (e) { + console.error(e) + throw e + } + } + getReadableError(result: ComfyResult): string { + const parseError = (error: any) => + `Error: ${error.message}\n${ + error.details ? `Details: ${error.details}\n` : '' + }` + + let errorMessage = result.error ? parseError(result.error) : '' + + if (result.node_errors) { + for (const [node_id, node_error] of Object.entries( + result.node_errors + )) { + errorMessage += `Node ${node_id}:\n${node_error.errors + .map(parseError) + .join('')}` + } + } + return errorMessage + } + + private getData(path: string[]): any[] { + let data = [] + try { + let obj = this.object_info + for (const p of path) { + obj = obj[p] + } + data = obj[0] + } catch (e) { + console.error( + `Failed to get data from path ${path.join('.')}: ${e}` + ) + } + return data + } + + getModels(): any[] { + return this.getData([ + 'CheckpointLoader', + 'input', + 'required', + 'ckpt_name', + ]) + } + + getVAEs(): any[] { + return this.getData(['VAELoader', 'input', 'required', 'vae_name']) + } + getSamplerNames(): string[] { + return this.getData(['KSampler', 'input', 'required', 'sampler_name']) + } + getHiResUpscalers(): string[] { + return this.getData([ + 'LatentUpscaleBy', + 'input', + 'required', + 'upscale_method', + ]) + } + getLoras(): string[] { + return this.getData(['LoraLoader', 'input', 'required', 'lora_name']) + } + async interrupt() { + const res = await requestPost(`${this.comfy_url}/interrupt`, {}) + console.log('res: ', res) + return res + } +} + +export default { + ComfyAPI, + comfy_api: new ComfyAPI(settings_tab.store.data.comfy_url), +} diff --git a/typescripts/comfyui/comfyui.tsx b/typescripts/comfyui/comfyui.tsx new file mode 100644 index 00000000..0ba6f733 --- /dev/null +++ b/typescripts/comfyui/comfyui.tsx @@ -0,0 +1,1678 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import { requestGet, requestPost } from '../util/ts/api' +import { observer } from 'mobx-react' +import { runInAction } from 'mobx' +import { + MoveToCanvasSvg, + SliderType, + SpMenu, + SpSlider, + SpSliderWithLabel, + SpTextfield, +} from '../util/elements' +import { ErrorBoundary } from '../util/errorBoundary' +import { Collapsible } from '../util/collapsible' +import Locale from '../locale/locale' + +import { Grid } from '../util/grid' +import { io } from '../util/oldSystem' +import { app } from 'photoshop' +import { reaction, toJS } from 'mobx' +import { storage } from 'uxp' + +import util, { + ComfyInputType, + ComfyUINode, + InputTypeEnum, + ValidInput, + store, +} from './util' + +import { base64UrlToBase64, copyJson, urlToCanvas } from '../util/ts/general' +import comfyapi from './comfyapi' +import { getSelectionInfoExe } from '../../psapi' +import { moveImageToLayer } from '../util/ts/io' + +interface Error { + type: string + message: string + details: string + extra_info: any +} + +interface NodeError { + errors: Error[] + dependent_outputs: string[] + class_type: string +} + +interface Result { + error?: Error + node_errors?: { [key: string]: NodeError } +} + +function logError(result: Result) { + // Top-level error + let has_error = false + let errorMessage = '' + + // Top-level error + if (result.error) { + errorMessage += `Error: ${result.error.message}\n` + has_error = true + if (result.error.details) { + errorMessage += `Details: ${result.error.details}\n` + } + } + + // Node errors + if (result.node_errors) { + for (const [node_id, node_error] of Object.entries( + result.node_errors + )) { + errorMessage += `Node ${node_id}:\n` + has_error = true + for (const error of node_error.errors) { + errorMessage += `- Error: ${error.message}\n` + if (error.details) { + errorMessage += ` Details: ${error.details}\n` + } + } + } + } + + if (errorMessage) { + app.showAlert(errorMessage) + } + return has_error +} + +export async function workflowEntries() { + try { + const workflow_folder = await storage.localFileSystem.getFolder() + + let entries = await workflow_folder.getEntries() + const workflow_entries = entries.filter( + // (e: any) => e.isFile && e.name.toLowerCase().includes('.png') // must be a file and has the of the type .png + (e: any) => e.isFile && e.name.toLowerCase().includes('.json') // must be a file and has the of the type .json + ) + + console.log('workflow_entries: ', workflow_entries) + + return workflow_entries + } catch (e) { + console.error(e) + // throw e + return [] + } +} +export async function postPrompt(prompt: any) { + try { + const url = 'http://127.0.0.1:8188/prompt' + const payload = { + prompt: prompt, + } + const result = await requestPost(url, payload) + + return result + } catch (e) { + console.error(e) + } +} + +const timer = (ms: any) => new Promise((res) => setTimeout(res, ms)) + +export async function generateRequest(prompt: any) { + try { + const prompt_result = await postPrompt(prompt) + const prompt_id = prompt_result.prompt_id + let history_result: any + let numberOfAttempts = 0 + let has_error = logError(prompt_result) + while (true && !has_error) { + try { + console.log('get history attempt: ', numberOfAttempts) + history_result = await util.getHistory(prompt_id) + console.log('history_result:', history_result) + if (history_result?.[prompt_id]) break + + numberOfAttempts += 1 + await timer(5000) + } catch (e) { + console.log('getHistory failed, retrying...') + } + } + return { history_result: history_result, prompt_id: prompt_id } + } catch (e) { + console.error(e) + } +} +export async function generateImage(prompt: any) { + try { + let { history_result, prompt_id }: any = await generateRequest(prompt) + + const outputs: any[] = Object.values(history_result[prompt_id].outputs) + const images: any[] = [] + for (const output of outputs) { + if (Array.isArray(output.images)) { + images.push(...output.images) + } + } + + const base64_imgs = [] + const formats: string[] = [] + + for (const image of images) { + const img = await comfyapi.comfy_api.view( + image.filename, + image.type, + image.subfolder + ) + base64_imgs.push(img) + formats.push(util.getFileFormat(image.filename)) + } + + store.data.comfyui_output_images = base64_imgs + + const thumbnails = [] + for (let i = 0; i < base64_imgs.length; ++i) { + if (['png', 'webp', 'jpg'].includes(formats[i])) { + thumbnails.push(await io.createThumbnail(base64_imgs[i], 300)) + } else if (['gif'].includes(formats[i])) { + thumbnails.push('data:image/gif;base64,' + base64_imgs[i]) + } + } + + store.data.comfyui_output_thumbnail_images = thumbnails + + return base64_imgs + } catch (e) { + console.error(e) + } +} + +export async function getConfig() { + //TODO: replace this method with get_object_info from comfyapi + try { + const prompt = { + '1': { + inputs: {}, + class_type: 'GetConfig', + }, + } + + let { history_result, prompt_id }: any = await generateRequest(prompt) + //@ts-ignore + const config: ComfyUIConfig = Object.values( + history_result?.[prompt_id].outputs + )[0] + store.data.comfyui_config = { ...config } + store.data.comfyui_config.valid_nodes = JSON.parse( + config.valid_nodes.join('') + ) + + return config + } catch (e) { + console.error(e) + } +} +export async function getWorkflowApi(image_path: string) { + try { + const prompt = { + '1': { + inputs: { + // image: 'C:/Users/abdul/Downloads/img2img_workflow.png', + image_path: image_path, + }, + class_type: 'LoadImageWithMetaData', + }, + } + let { history_result, prompt_id }: any = await generateRequest(prompt) + //@ts-ignore + let { prompt: result_prompt, workflow } = Object.values( + history_result?.[prompt_id].outputs + )[0] + result_prompt = JSON.parse(result_prompt.join('')) + workflow = JSON.parse(workflow.join('')) + + return { prompt: result_prompt, workflow } + } catch (e) { + console.error(e) + } +} +function filterObjectProperties(node_inputs: any, valid_keys: string[]) { + return Object.fromEntries( + valid_keys.map((key: string) => [key, node_inputs[key]]) + ) +} +export function parseUIFromNode(node: ComfyUINode, node_id: string) { + //convert node to array of ui element definition + try { + const ValidNodes = store.data.comfyui_config?.valid_nodes + const valid_ui = ValidNodes[node.class_type]?.inputs // all the valid inputs of a node + const list_ids = ValidNodes[node.class_type]?.list_id + if (valid_ui) { + const keys = Object.keys(valid_ui) + const filtered_values = filterObjectProperties(node.inputs, keys) + const entires = keys.map((key) => { + //example values: + // "sampler_name": { + // "label": "sampler_name", + // "value": "dpmpp_2m", + // "type": "Menu", + // "list_id": "samplers" + // }, + + return [ + key, + { + label: key, + value: filtered_values[key], + type: valid_ui[key], + list_id: list_ids?.[key], + node_id: node_id, + }, + ] + }) + + const valid_node_input = Object.fromEntries(entires) + store.data.comfyui_valid_nodes[node_id] = valid_node_input + store.data.uuids[node_id] = window.crypto.randomUUID() + return valid_node_input + } + } catch (e) { + console.error(e) + } +} + +export function storeToPrompt(store: any, basePrompt: any) { + //TODO change .map to .forEach + let modified_prompt = { ...basePrompt } // the original prompt but with the value of the ui + Object.entries(store.data.comfyui_valid_nodes).forEach( + ([node_id, node_inputs]: [string, any]) => { + Object.entries(node_inputs).forEach( + ([input_id, node_input]: [string, any]) => { + return (modified_prompt[node_id]['inputs'][input_id] = + node_input.value) + } + ) + } + ) + // store.data. + // modified_propmt[load_image_node_id] + // prompt = { ...store.data.comfyui_valid_nodes } + + return modified_prompt +} +function createMenu(input: ValidInput) { + console.log('input: ', toJS(input)) + return ( + <> + + {input.label} + + { + input.value = value.item + }} + > + + ) +} + +function createTextField(input: ValidInput) { + let element = ( + <> + {input.label} + { + input.value = evt.target.value + }} + > + + ) + return element +} +function createTextArea(input: ValidInput) { + let element = ( + <> + {input.label} + { + input.value = event.target.value + }} + placeholder={`${input.label}`} + value={input.value} + > + + ) + return element +} +function createImageBase64(input: ValidInput) { + let element = ( + <> +
+ +
+
+ +
+ + ) + return element +} +function nodeInputToHtmlElement(input: ValidInput) { + let element + if ( + [InputTypeEnum.NumberField, InputTypeEnum.TextField].includes( + input.type + ) + ) { + element = createTextField(input) + } else if ([InputTypeEnum.Menu].includes(input.type)) { + element = createMenu(input) + } else if ([InputTypeEnum.TextArea].includes(input.type)) { + element = createTextArea(input) + } else if ([InputTypeEnum.ImageBase64].includes(input.type)) { + element = createImageBase64(input) + } + return element +} + +export async function loadWorkflow(workflow_path: string) { + try { + store.data.current_prompt = ( + await getWorkflowApi(workflow_path) + )?.prompt + + store.data.is_random_seed = Object.fromEntries( + Object.keys(toJS(store.data.current_prompt)).map( + (node_id: string) => { + return [node_id, false] + } + ) + ) + const current_prompt: any = store.toJsFunc().data.current_prompt + const loadImageNodes = Object.keys(current_prompt) + .filter( + (key: any) => current_prompt[key].class_type === 'LoadImage' + ) + .reduce( + (acc, key) => ({ + ...acc, + [key]: current_prompt[key], + }), + {} + ) + + Object.keys(loadImageNodes).forEach((node_id: any) => { + store.data.current_prompt[node_id] = { + inputs: { + image_base64: '', + }, + class_type: 'LoadImageBase64', + } + }) + + const node_obj = Object.entries(store.data.current_prompt) + //clear both node structure and base64 images store values + store.data.comfyui_valid_nodes = {} + store.data.uuids = {} + // store.data.load_image_base64_strings = {} + node_obj.forEach(([node_id, node]: [string, any]) => { + console.log(node_id, node) + const valid_input = parseUIFromNode(node, node_id) + }) + } catch (e) { + console.error(e) + } +} + +function setSliderValue(store: any, node_id: string, name: string, value: any) { + runInAction(() => { + store.data.current_prompt2[node_id].inputs[name] = value + }) +} +async function onChangeLoadImage(node_id: string, filename: string) { + try { + store.data.current_uploaded_image[node_id] = + await util.base64UrlFromComfy({ + filename: encodeURIComponent(filename), + type: 'input', + subfolder: '', + }) + store.data.current_prompt2[node_id].inputs.image = filename + } catch (e) { + console.warn(e) + } +} +async function onChangeLoadVideo(node_id: string, filename: string) { + try { + store.data.current_uploaded_video[node_id] = + await util.base64UrlFromComfy({ + filename: encodeURIComponent(filename), + type: 'input', + subfolder: '', + }) + store.data.current_prompt2[node_id].inputs.video = filename + } catch (e) { + console.warn(e) + } +} +function renderNode(node_id: string, node: any, is_output: boolean) { + const comfy_node_info = toJS(store.data.object_info[node.class_type]) + + // console.log('comfy_node_info: ', comfy_node_info) + const node_type = util.getNodeType(node.class_type) + let node_html + + const inputs = toJS(node.inputs) + node_html = Object.entries(inputs).map(([name, value], index) => { + // store.data.current_prompt2[node_id].inputs[name] = value + try { + const input = comfy_node_info.input.required[name] + + let { type, config } = util.parseComfyInput(name, input, value) + if (type === ComfyInputType.Skip) { + return ( +
+ ) + } + const html_element = renderInput( + node_id, + name, + type, + config, + `${node_id}_${name}_${type}_${index}` + ) + + return html_element + } catch (e) { + console.error(e) + return ( +
+ {/* {name},{type}, {JSON.stringify(config)} */} +
+ ) + } + }) + + if (node_type === util.ComfyNodeType.LoadImage) { + const uploaded_images = store.data.uploaded_images_list + const inputs = store.data.current_prompt2[node_id].inputs + const node_name = node.class_type + node_html = ( +
+ {/* New load image component */} +
+ + +
+ + {node_name} + +
+ + ) => { + console.log('onChange value.item: ', item) + inputs.image = item + //load image store for each LoadImage Node + //use node_id to store these + + onChangeLoadImage(node_id, item) + }} + > +
+
+ + + +
+ { + console.error( + 'error loading image: ', + store.data.current_uploaded_image[node_id] + ) + + onChangeLoadImage(node_id, inputs.image) + }} + /> +
+ ) + } else if (node_type === util.ComfyNodeType.LoadVideo) { + //ToDo: rewrite this to support video loading node + const uploaded_video_list = store.data.uploaded_video_list + const inputs = store.data.current_prompt2[node_id].inputs + const node_name = node.class_type + node_html = ( +
+ {/* New load Video component */} +
+ +
+ + {node_name} + +
+ + ) => { + console.log('onChange value.item: ', item) + inputs.video = item + + onChangeLoadVideo(node_id, item) + }} + > +
+
+ + + +
+ { + console.error( + 'error loading video: ', + store.data.current_uploaded_video[node_id] + ) + + onChangeLoadVideo(node_id, inputs.video) + }} + /> +
+ ) + } + + if (is_output) { + const output_node_element = ( +
+ {node_html} + {store.data.current_prompt2_output[node_id]?.length > 0 ? ( + { + store.data.output_thumbnail_image_size[node_id] = + evt.target.value + }} + > + + Thumbnail Size: + + + {parseInt( + store.data.output_thumbnail_image_size[ + node_id + ] as any + )} + + + ) : ( + void 0 + )} + { + let format = util.extractFormat( + store.data.current_prompt2_output[node_id][ + index + ] + ) + + if (format === 'gif') { + //@ts-ignore + openFileFromUrlExe( + store.data.current_prompt2_output[ + node_id + ][index], + format + ) + } else if (format === 'png') { + urlToCanvas( + store.data.current_prompt2_output[ + node_id + ][index], + 'comfy_output.png' + ) + } + }, + title: 'Copy Image to Canvas', + }, + { + ComponentType: MoveToCanvasSvg, + callback: async (index: number) => { + let format = util.extractFormat( + store.data.current_prompt2_output[node_id][ + index + ] + ) + + if (format === 'png') { + const selectionInfo = + await getSelectionInfoExe() + const image_layer = await moveImageToLayer( + base64UrlToBase64( + store.data.current_prompt2_output[ + node_id + ][index] + ), + selectionInfo + ) + } + }, + title: 'Copy Image to Selection Area', + }, + ]} + > +
+ ) + + return output_node_element + } + return node_html +} +function renderInput( + node_id: string, + name: string, + type: any, + config: any, + key?: string +) { + let html_element = ( +
+ {name},{type}, {JSON.stringify(config)} +
+ ) + const inputs = store.data.current_prompt2[node_id].inputs + + if (type === util.ComfyInputType.Seed) { + html_element = ( + <> + {name}: + { + // store.data.search_query = event.target.value + inputs[name] = event.target.value + console.log(`${name}: ${event.target.value}`) + }} + > + { + store.data.is_random_seed[node_id] = evt.target.checked + }} + style={{ display: 'inline-flex' }} + > + random + + + ) + } else if (type === util.ComfyInputType.BigNumber) { + html_element = ( + <> + {name}: + { + // store.data.search_query = event.target.value + inputs[name] = event.target.value + console.log(`${name}: ${event.target.value}`) + }} + > + + ) + } else if (type === util.ComfyInputType.TextFieldNumber) { + html_element = ( + <> + {name}: + { + const v = e.target.value + let new_value = + v !== '' + ? Math.max(config.min, Math.min(config.max, v)) + : v + inputs[name] = new_value + + console.log(`${name}: ${e.target.value}`) + }} + > + + ) + } else if (type === util.ComfyInputType.Slider) { + html_element = ( + { + // inputs[name] = new_value + // setSliderValue(store, node_id, name, new_value) + store.data.current_prompt2[node_id].inputs[name] = new_value + console.log('slider_change: ', new_value) + }} + /> + ) + } else if (type === util.ComfyInputType.Menu) { + html_element = ( + <> + + {name} + + + ) => { + console.log('onChange value.item: ', item) + inputs[name] = item + }} + > + + ) + } else if (type === util.ComfyInputType.TextArea) { + html_element = ( + { + try { + // this.changePositivePrompt( + // event.target.value, + // store.data.current_index + // ) + // autoResize( + // event.target, + // store.data.positivePrompts[ + // store.data.current_index + // ] + // ) + inputs[name] = event.target.value + } catch (e) { + console.warn(e) + } + }} + placeholder={`${name}`} + value={inputs[name]} + > + ) + } else if (type === util.ComfyInputType.TextField) { + html_element = ( + <> + {name}: + + { + inputs[name] = e.target.value + console.log(`${name}: ${e.target.value}`) + }} + > + + ) + } else if (type === util.ComfyInputType.CheckBox) { + html_element = ( + <> + {/* {name}: */} + + { + inputs[name] = evt.target.checked + }} + style={{ display: 'inline-flex' }} + > + {name} + + + ) + } + + return
{html_element}
+} + +export function swap(index1: number, index2: number) { + const { length } = store.data.nodes_order + if (index1 >= 0 && index1 < length && index2 >= 0 && index2 < length) { + ;[store.data.nodes_order[index1], store.data.nodes_order[index2]] = [ + store.data.nodes_order[index2], + store.data.nodes_order[index1], + ] + } +} + +export function saveWorkflowData( + workflow_name: string, + { prompt, nodes_order, nodes_label }: WorkflowData +) { + storage.localStorage.setItem( + workflow_name, + JSON.stringify({ prompt, nodes_order, nodes_label }) + ) +} +export function loadWorkflowData(workflow_name: string): WorkflowData { + const workflow_data: WorkflowData = JSON.parse( + storage.localStorage.getItem(workflow_name) + ) + return workflow_data +} +interface WorkflowData { + prompt: any + nodes_order: string[] + nodes_label: Record +} + +function resetWorkflowData(workflow_name: string) { + delete storage.localStorage[workflow_name] + // const workflow_name = store.data.selected_workflow_name + const original_workflow = store.data.workflows2[workflow_name] + loadWorkflow2(original_workflow) + // resetNodeOrder() +} +function loadWorkflow2(workflow: any) { + try { + //1) get prompt + store.data.current_prompt2 = copyJson(workflow) + + //2) get the original order + store.data.nodes_order = Object.keys(toJS(store.data.current_prompt2)) + + //3) get labels for each nodes + store.data.nodes_label = Object.fromEntries( + Object.entries(toJS(store.data.current_prompt2)).map( + ([node_id, node]: [string, any]) => { + if (!(node.class_type in store.data.object_info)) { + throw new Error( + `ComfyUI Node of type "${node.class_type}" is not found. Please ensure it is installed via the ComfyUI Manager.` + ) + } + + return [ + node_id, + toJS(store.data.object_info[node.class_type]) + .display_name, + ] + } + ) + ) + store.data.is_random_seed = Object.fromEntries( + Object.keys(toJS(store.data.current_prompt2)).map( + (node_id: string) => { + return [node_id, false] + } + ) + ) + + // parse the output nodes + // Note: we can't modify the node directly in the prompt like we do for input nodes. + //.. since this data doesn't exist on the prompt. so we create separate container for the output images + store.data.current_prompt2_output = Object.entries( + store.data.current_prompt2 + ).reduce( + ( + output_entries: Record, + [node_id, node]: [string, any] + ) => { + if (store.data.object_info[node.class_type].output_node) { + output_entries[node_id] = [] + } + return output_entries + }, + {} + ) + + //slider variables for output nodes + //TODO: delete store.data.output_thumbnail_image_size before loading a new workflow + for (let key in toJS(store.data.current_prompt2_output)) { + store.data.output_thumbnail_image_size[key] = 200 + } + + const workflow_name = store.data.selected_workflow_name + if (workflow_name) { + // check if the workflow has a name + + if (workflow_name in storage.localStorage) { + //load the workflow data from local storage + //1) load the last parameters used in generation + //2) load the order of the nodes + //3) load the labels of the nodes + + const workflow_data: WorkflowData = + loadWorkflowData(workflow_name) + if ( + util.isSameStructure( + workflow_data.prompt, + toJS(store.data.current_prompt2) + ) + ) { + //load 1) + store.data.current_prompt2 = workflow_data.prompt + //load 2) + store.data.nodes_order = workflow_data.nodes_order + //load 3) + store.data.nodes_label = workflow_data.nodes_label + } else { + // do not load. instead override the localStorage with the new values + workflow_data.prompt = toJS(store.data.current_prompt2) + workflow_data.nodes_order = toJS(store.data.nodes_order) + workflow_data.nodes_label = toJS(store.data.nodes_label) + + saveWorkflowData(workflow_name, workflow_data) + } + } else { + // if workflow data is missing from local storage then save it for next time. + //1) save parameters values + //2) save nodes order + //3) save nodes label + + const prompt = toJS(store.data.current_prompt2) + const nodes_order = toJS(store.data.nodes_order) + const nodes_label = toJS(store.data.nodes_label) + saveWorkflowData(workflow_name, { + prompt, + nodes_order, + nodes_label, + }) + } + } + } catch (e) { + console.error(e) + app.showAlert(`${e}`) + } +} + +async function getUploadedImages(images_list: string[]) { + //convert all of comfyui loaded images into base64url that the plugin can use + let uploaded_images = images_list + + uploaded_images = uploaded_images.filter((image_name: string) => { + let extension = image_name.split('.').pop() || '' + // return extension !== 'gif' && extension !== 'mp4' + return ['png', 'jpg', 'webp'].includes(extension) + }) + + const uploaded_images_base64_url: string[] = await Promise.all( + uploaded_images.map(async (filename: string) => { + try { + return await util.base64UrlFromComfy({ + filename: encodeURIComponent(filename), + type: 'input', + subfolder: '', + }) + } catch (e) { + console.warn(e) + return '' + } + }) + ) + store.data.uploaded_images_list = + store.data.object_info.LoadImage.input.required['image'][0] + + store.data.uploaded_images_base64_url = uploaded_images_base64_url + return store.data.uploaded_images_base64_url +} + +function getBorderColor(is_output: boolean, last_moved: boolean) { + let color: string | undefined = void 0 + if (last_moved) { + // color = '#2c4639' + color = '#e34d12' + + return color + } + if (is_output) { + // color = '#e34d12' + color = '#6db579' + } else { + color = '#6d6c6c' + } + + return color +} +@observer +class ComfyWorkflowComponent extends React.Component<{}, { value?: number }> { + async componentDidMount(): Promise { + try { + store.data.object_info = await comfyapi.comfy_api.init() + // getUploadedImages( + // store.data.object_info.LoadImage.input.required['image'][0] + // ) + if (store.data.object_info?.VHS_LoadVideo) { + store.data.uploaded_video_list = + store.data.object_info.VHS_LoadVideo.input.required[ + 'video' + ][0] + } + } catch (e) { + console.error(e) + } + } + + render(): React.ReactNode { + return ( +
+
+ {/* {util.getNodes(util.hi_res_workflow).map((node, index) => { + // return
{node.class_type}
+ return ( +
{this.renderNode(node)}
+ ) + })} */} + + + + +
+
+ +
+
+ { + store.data.selected_workflow_name = value.item + loadWorkflow2(store.data.workflows2[value.item]) + }} + >{' '} + + +
+ + {store.data.object_info ? ( + <> +
+ {util + .getNodes(store.data.current_prompt2) + .sort( + ([node_id1, node1], [node_id2, node2]) => { + return ( + store.data.nodes_order.indexOf( + node_id1 + ) - + store.data.nodes_order.indexOf( + node_id2 + ) + ) + } + ) + + .map(([node_id, node], index) => { + try { + const is_output = + store.data.object_info[ + node.class_type + ].output_node + return ( +
+
+
+ +
+
+ + {/* */} +
+
+ + "{node_id}":{' '} + + { + store.data + .nodes_label[ + node_id + ] + } + {' '} + {' '} + + {node.class_type} + +
+ { + store.data.nodes_label[ + node_id + ] = + event.target.value + }} + > +
+ {renderNode( + node_id, + node, + is_output + )} +
+ ) + } catch (e) { + console.error( + `node_id: ${node_id}`, + 'node:', + toJS(node), + 'node.class_type', + node.class_type, + 'store.data.object_info[node.class_type]: ', + store.data.object_info[ + node.class_type + ], + 'error: ', + e + ) + return ( + + {Locale( + `Error: Node ${node.class_type} is + missing, please install it from + comfyui manager` + )} + + ) + } + })} +
+ + ) : ( + void 0 + )} +
+ ) + } +} +const container = document.getElementById('ComfyUIContainer')! +const root = ReactDOM.createRoot(container) +root.render( + // + +
+ + {/* */} + + + +
+ {/* */} +
+ //
+) diff --git a/typescripts/comfyui/img2img_api.json b/typescripts/comfyui/img2img_api.json new file mode 100644 index 00000000..9ea4f235 --- /dev/null +++ b/typescripts/comfyui/img2img_api.json @@ -0,0 +1,299 @@ +{ + "3": { + "inputs": { + "seed": 532429388244110, + "steps": 20, + "cfg": 8, + "sampler_name": "dpmpp_sde", + "scheduler": "karras", + "denoise": 0.72, + "model": [ + "76", + 0 + ], + "positive": [ + "91", + 3 + ], + "negative": [ + "91", + 4 + ], + "latent_image": [ + "63", + 0 + ] + }, + "class_type": "KSampler" + }, + "6": { + "inputs": { + "text": [ + "76", + 2 + ], + "clip": [ + "76", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "text": [ + "78", + 2 + ], + "clip": [ + "78", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "8": { + "inputs": { + "samples": [ + "3", + 0 + ], + "vae": [ + "57", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "9": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": [ + "8", + 0 + ] + }, + "class_type": "SaveImage" + }, + "11": { + "inputs": { + "seed": 4820153955672, + "steps": 20, + "cfg": 8, + "sampler_name": "dpmpp_2m", + "scheduler": "simple", + "denoise": 0.4, + "model": [ + "76", + 0 + ], + "positive": [ + "91", + 3 + ], + "negative": [ + "91", + 4 + ], + "latent_image": [ + "55", + 0 + ] + }, + "class_type": "KSampler" + }, + "12": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": [ + "13", + 0 + ] + }, + "class_type": "SaveImage" + }, + "13": { + "inputs": { + "samples": [ + "11", + 0 + ], + "vae": [ + "57", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "16": { + "inputs": { + "ckpt_name": "dreamshaper_8.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "55": { + "inputs": { + "upscale_method": "nearest-exact", + "scale_by": 2, + "samples": [ + "3", + 0 + ] + }, + "class_type": "LatentUpscaleBy" + }, + "57": { + "inputs": { + "vae_name": "MoistMix.vae.pt" + }, + "class_type": "VAELoader" + }, + "58": { + "inputs": { + "image": "Layer 3 (1).png", + "choose file to upload": "image" + }, + "class_type": "LoadImage" + }, + "59": { + "inputs": { + "pixels": [ + "60", + 0 + ], + "vae": [ + "57", + 0 + ] + }, + "class_type": "VAEEncode" + }, + "60": { + "inputs": { + "upscale_method": "nearest-exact", + "width": 512, + "height": 512, + "crop": "disabled", + "image": [ + "58", + 0 + ] + }, + "class_type": "ImageScale" + }, + "63": { + "inputs": { + "amount": 1, + "samples": [ + "59", + 0 + ] + }, + "class_type": "RepeatLatentBatch" + }, + "76": { + "inputs": { + "prompt": "", + "model": [ + "78", + 0 + ], + "clip": [ + "109", + 0 + ] + }, + "class_type": "LoadLorasFromPrompt" + }, + "78": { + "inputs": { + "prompt": "bad hands, text, watermark", + "model": [ + "16", + 0 + ], + "clip": [ + "109", + 0 + ] + }, + "class_type": "LoadLorasFromPrompt" + }, + "88": { + "inputs": { + "images": [ + "91", + 0 + ] + }, + "class_type": "PreviewImage" + }, + "89": { + "inputs": { + "images": [ + "91", + 1 + ] + }, + "class_type": "PreviewImage" + }, + "90": { + "inputs": { + "images": [ + "91", + 2 + ] + }, + "class_type": "PreviewImage" + }, + "91": { + "inputs": { + "is_enabled_1": "disable", + "preprocessor_name_1": "CannyEdgePreprocessor", + "control_net_name_1": "control_lora_rank128_v11p_sd15_canny_fp16.safetensors", + "strength_1": 1, + "threshold_a_1": 100, + "threshold_b_1": 200, + "start_percent_1": 0, + "end_percent_1": 1, + "resolution_1": 512, + "is_enabled_2": "disable", + "preprocessor_name_2": "OpenposePreprocessor", + "control_net_name_2": "control_lora_rank128_v11p_sd15_openpose_fp16.safetensors", + "strength_2": 1, + "threshold_a_2": 0, + "threshold_b_2": 0, + "start_percent_2": 0, + "end_percent_2": 1, + "resolution_2": 512, + "is_enabled_3": "disable", + "preprocessor_name_3": "InpaintPreprocessor", + "control_net_name_3": "control_lora_rank128_v11p_sd15_inpaint_fp16.safetensors", + "strength_3": 1, + "threshold_a_3": 0, + "threshold_b_3": 0, + "start_percent_3": 0, + "end_percent_3": 1, + "resolution_3": 512, + "positive": [ + "6", + 0 + ], + "negative": [ + "7", + 0 + ] + }, + "class_type": "ControlNetScript" + }, + "109": { + "inputs": { + "stop_at_clip_layer": -1, + "clip": [ + "16", + 1 + ] + }, + "class_type": "CLIPSetLastLayer" + } +} \ No newline at end of file diff --git a/typescripts/comfyui/img2img_workflow.json b/typescripts/comfyui/img2img_workflow.json new file mode 100644 index 00000000..8776d70a --- /dev/null +++ b/typescripts/comfyui/img2img_workflow.json @@ -0,0 +1,2159 @@ +{ + "last_node_id": 109, + "last_link_id": 204, + "nodes": [ + { + "id": 8, + "type": "VAEDecode", + "pos": [ + 1238.8565730529783, + 603.497425166778 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 32, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 36 + }, + { + "name": "vae", + "type": "VAE", + "link": 82 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 9 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 13, + "type": "VAEDecode", + "pos": [ + 1961, + 125 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 36, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 15 + }, + { + "name": "vae", + "type": "VAE", + "link": 83 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 17 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 66, + "type": "Reroute", + "pos": [ + 210, + 188 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 21, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 134 + } + ], + "outputs": [ + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 100 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 57, + "type": "VAELoader", + "pos": [ + -194, + 692 + ], + "size": { + "0": 315, + "1": 58 + }, + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 142 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "Load VAE | id:vae", + "properties": { + "Node name for S&R": "VAELoader" + }, + "widgets_values": [ + "MoistMix.vae.pt" + ] + }, + { + "id": 56, + "type": "Reroute", + "pos": [ + 247, + 639 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 11, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 142 + } + ], + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 82, + 83, + 85 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 65, + "type": "Reroute", + "pos": [ + 237, + 92 + ], + "size": [ + 82, + 26 + ], + "flags": {}, + "order": 20, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 133 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 94, + 95 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 78, + "type": "LoadLorasFromPrompt", + "pos": [ + -261, + 422 + ], + "size": { + "0": 462, + "1": 200 + }, + "flags": {}, + "order": 17, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 193 + }, + { + "name": "clip", + "type": "CLIP", + "link": 204 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 140 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 138 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "STRING", + "type": "STRING", + "links": [ + 137 + ], + "shape": 3, + "slot_index": 2 + } + ], + "title": "Load Loras From Prompt | id:multi_loras_negative_prompt", + "properties": { + "Node name for S&R": "LoadLorasFromPrompt" + }, + "widgets_values": [ + "bad hands, text, watermark" + ] + }, + { + "id": 11, + "type": "KSampler", + "pos": [ + 1587.0000488281248, + 125.99995765686035 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 35, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 95, + "slot_index": 0 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 153, + "slot_index": 1 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 155, + "slot_index": 2 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 79, + "slot_index": 3 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 15 + ], + "slot_index": 0 + } + ], + "title": "KSampler | id:hires_sampler", + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 4820153955672, + "fixed", + 20, + 8, + "dpmpp_2m", + "simple", + 0.4 + ] + }, + { + "id": 85, + "type": "Reroute", + "pos": [ + 604, + -327 + ], + "size": [ + 140.8, + 26 + ], + "flags": {}, + "order": 30, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 199, + "slot_index": 0 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 153, + 154 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 7, + "type": "CLIPTextEncode", + "pos": [ + 638.8565730529789, + 359.49742516677856 + ], + "size": { + "0": 425.27801513671875, + "1": 180.6060791015625 + }, + "flags": {}, + "order": 19, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 138 + }, + { + "name": "text", + "type": "STRING", + "link": 137, + "widget": { + "name": "text" + } + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 171 + ], + "slot_index": 0 + } + ], + "title": "CLIP Text Encode (Prompt) | id:negative_prompt", + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "bad hands, text, watermark\n" + ] + }, + { + "id": 86, + "type": "Reroute", + "pos": [ + 612, + -277 + ], + "size": [ + 140.8, + 26 + ], + "flags": {}, + "order": 29, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 174, + "slot_index": 0 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 155, + 156 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 55, + "type": "LatentUpscaleBy", + "pos": [ + 1465, + -133 + ], + "size": { + "0": 315, + "1": 82 + }, + "flags": {}, + "order": 33, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 78 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 79 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "Upscale Latent By | id:scaler", + "properties": { + "Node name for S&R": "LatentUpscaleBy" + }, + "widgets_values": [ + "nearest-exact", + 2 + ] + }, + { + "id": 93, + "type": "LoadImage", + "pos": [ + -1630, + -1670 + ], + "size": { + "0": 277.20001220703125, + "1": 314 + }, + "flags": {}, + "order": 1, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 167 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_mask_2", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + }, + { + "id": 95, + "type": "LoadImage", + "pos": [ + -1690, + -1230 + ], + "size": { + "0": 277.20001220703125, + "1": 314 + }, + "flags": {}, + "order": 2, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 169 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_mask_3", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + }, + { + "id": 6, + "type": "CLIPTextEncode", + "pos": [ + 637, + 123 + ], + "size": { + "0": 422.84503173828125, + "1": 164.31304931640625 + }, + "flags": {}, + "order": 22, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 100 + }, + { + "name": "text", + "type": "STRING", + "link": 135, + "widget": { + "name": "text" + } + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 196 + ], + "slot_index": 0 + } + ], + "title": "CLIP Text Encode (Prompt) | id:positive_prompt", + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "lora_bright_style girl" + ] + }, + { + "id": 63, + "type": "RepeatLatentBatch", + "pos": [ + 800, + -413 + ], + "size": { + "0": 315, + "1": 58 + }, + "flags": {}, + "order": 16, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 200 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 92 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "Repeat Latent Batch | id:latent_batch", + "properties": { + "Node name for S&R": "RepeatLatentBatch" + }, + "widgets_values": [ + 1 + ] + }, + { + "id": 106, + "type": "unCLIPConditioning", + "pos": [ + -1619, + -720 + ], + "size": { + "0": 393, + "1": 102 + }, + "flags": {}, + "order": 23, + "mode": 4, + "inputs": [ + { + "name": "conditioning", + "type": "CONDITIONING", + "link": 196, + "slot_index": 0 + }, + { + "name": "clip_vision_output", + "type": "CLIP_VISION_OUTPUT", + "link": 194 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 195 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "unCLIPConditioning" + }, + "widgets_values": [ + 1, + 0 + ] + }, + { + "id": 9, + "type": "SaveImage", + "pos": [ + 1495, + 584 + ], + "size": { + "0": 232.94032287597656, + "1": 282.4336242675781 + }, + "flags": {}, + "order": 34, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9 + } + ], + "properties": {}, + "widgets_values": [ + "ComfyUI" + ] + }, + { + "id": 107, + "type": "unCLIPConditioning", + "pos": [ + -283, + -852 + ], + "size": { + "0": 393, + "1": 102 + }, + "flags": {}, + "order": 28, + "mode": 4, + "inputs": [ + { + "name": "conditioning", + "type": "CONDITIONING", + "link": 198, + "slot_index": 0 + }, + { + "name": "clip_vision_output", + "type": "CLIP_VISION_OUTPUT", + "link": 197, + "slot_index": 1 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 199 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "unCLIPConditioning" + }, + "widgets_values": [ + 1, + 0 + ] + }, + { + "id": 104, + "type": "IPAdapter", + "pos": [ + -503, + -288 + ], + "size": { + "0": 380.4000244140625, + "1": 166 + }, + "flags": {}, + "order": 15, + "mode": 4, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 192, + "slot_index": 0 + }, + { + "name": "image", + "type": "IMAGE", + "link": 190 + }, + { + "name": "clip_vision", + "type": "CLIP_VISION", + "link": 191, + "slot_index": 2 + }, + { + "name": "mask", + "type": "MASK", + "link": null + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 193 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CLIP_VISION_OUTPUT", + "type": "CLIP_VISION_OUTPUT", + "links": [ + 194, + 197 + ], + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "IPAdapter" + }, + "widgets_values": [ + 1, + "ip-adapter-plus_sd15.bin", + "fp16" + ] + }, + { + "id": 105, + "type": "CLIPVisionLoader", + "pos": [ + -923, + -242 + ], + "size": { + "0": 315, + "1": 58 + }, + "flags": {}, + "order": 3, + "mode": 4, + "outputs": [ + { + "name": "CLIP_VISION", + "type": "CLIP_VISION", + "links": [ + 191 + ], + "shape": 3 + } + ], + "properties": { + "Node name for S&R": "CLIPVisionLoader" + }, + "widgets_values": [ + "model.safetensors" + ] + }, + { + "id": 60, + "type": "ImageScale", + "pos": [ + -554, + -43 + ], + "size": { + "0": 315, + "1": 130 + }, + "flags": {}, + "order": 12, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 86 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 87, + 190 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "Upscale Image | id:init_image_scale", + "properties": { + "Node name for S&R": "ImageScale" + }, + "widgets_values": [ + "nearest-exact", + 512, + 512, + "disabled" + ] + }, + { + "id": 59, + "type": "VAEEncode", + "pos": [ + 523, + -531 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 14, + "mode": 0, + "inputs": [ + { + "name": "pixels", + "type": "IMAGE", + "link": 87 + }, + { + "name": "vae", + "type": "VAE", + "link": 85 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 200 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEEncode" + } + }, + { + "id": 103, + "type": "EmptyLatentImage", + "pos": [ + 514, + -716 + ], + "size": { + "0": 315, + "1": 106 + }, + "flags": {}, + "order": 4, + "mode": 2, + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 512, + 512, + 1 + ] + }, + { + "id": 3, + "type": "KSampler", + "pos": [ + 928, + -292 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 31, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 94 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 154 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 156 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 92 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 36, + 78 + ], + "slot_index": 0 + } + ], + "title": "KSampler | id:sampler", + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 532429388244110, + "fixed", + 20, + 8, + "dpmpp_sde", + "karras", + 0.72 + ] + }, + { + "id": 92, + "type": "LoadImage", + "pos": [ + -1960, + -2190 + ], + "size": { + "0": 277.20001220703125, + "1": 314 + }, + "flags": { + "collapsed": false + }, + "order": 5, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 165 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_mask_1", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + }, + { + "id": 58, + "type": "LoadImage", + "pos": [ + -1328, + -135 + ], + "size": { + "0": 315, + "1": 314 + }, + "flags": {}, + "order": 6, + "mode": 0, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 86 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:init_image", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "Layer 3 (1).png", + "image" + ] + }, + { + "id": 89, + "type": "PreviewImage", + "pos": [ + -280, + -1260 + ], + "size": { + "0": 336, + "1": 246 + }, + "flags": {}, + "order": 26, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 162 + } + ], + "title": "Preview Image | id:preprocessor_output_2", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 90, + "type": "PreviewImage", + "pos": [ + -40, + -1260 + ], + "size": { + "0": 336, + "1": 246 + }, + "flags": {}, + "order": 27, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 163 + } + ], + "title": "Preview Image | id:preprocessor_output_3", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 88, + "type": "PreviewImage", + "pos": [ + -530, + -1260 + ], + "size": { + "0": 336, + "1": 246 + }, + "flags": {}, + "order": 25, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 161 + } + ], + "title": "Preview Image | id:preprocessor_output_1", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 12, + "type": "SaveImage", + "pos": [ + 2367, + 49 + ], + "size": { + "0": 407.53717041015625, + "1": 468.13226318359375 + }, + "flags": {}, + "order": 37, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 17 + } + ], + "title": "Save Image | id:hires_output", + "properties": {}, + "widgets_values": [ + "ComfyUI" + ] + }, + { + "id": 91, + "type": "ControlNetScript", + "pos": [ + -1381, + -1042 + ], + "size": { + "0": 506.0240173339844, + "1": 822 + }, + "flags": {}, + "order": 24, + "mode": 0, + "inputs": [ + { + "name": "positive", + "type": "CONDITIONING", + "link": 195 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 171, + "slot_index": 1 + }, + { + "name": "image_1", + "type": "IMAGE", + "link": 201 + }, + { + "name": "mask_1", + "type": "IMAGE", + "link": 165, + "slot_index": 3 + }, + { + "name": "image_2", + "type": "IMAGE", + "link": 166 + }, + { + "name": "mask_2", + "type": "IMAGE", + "link": 167 + }, + { + "name": "image_3", + "type": "IMAGE", + "link": 168 + }, + { + "name": "mask_3", + "type": "IMAGE", + "link": 169 + } + ], + "outputs": [ + { + "name": "preprocessed_image_1", + "type": "IMAGE", + "links": [ + 161 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "preprocessed_image_2", + "type": "IMAGE", + "links": [ + 162 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "preprocessed_image_3", + "type": "IMAGE", + "links": [ + 163 + ], + "shape": 3, + "slot_index": 2 + }, + { + "name": "positive", + "type": "CONDITIONING", + "links": [ + 198 + ], + "shape": 3, + "slot_index": 3 + }, + { + "name": "negative", + "type": "CONDITIONING", + "links": [ + 174 + ], + "shape": 3, + "slot_index": 4 + } + ], + "title": "ControlNet Script | id:controlnet_script", + "properties": { + "Node name for S&R": "ControlNetScript" + }, + "widgets_values": [ + "disable", + "CannyEdgePreprocessor", + "control_lora_rank128_v11p_sd15_canny_fp16.safetensors", + 1, + 100, + 200, + 0, + 1, + 512, + "disable", + "OpenposePreprocessor", + "control_lora_rank128_v11p_sd15_openpose_fp16.safetensors", + 1, + 0, + 0, + 0, + 1, + 512, + "disable", + "InpaintPreprocessor", + "control_lora_rank128_v11p_sd15_inpaint_fp16.safetensors", + 1, + 0, + 0, + 0, + 1, + 512 + ] + }, + { + "id": 16, + "type": "CheckpointLoaderSimple", + "pos": [ + -1297, + 369 + ], + "size": { + "0": 315, + "1": 98 + }, + "flags": {}, + "order": 7, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 192 + ], + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 202 + ], + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [], + "slot_index": 2 + } + ], + "title": "Load Checkpoint | id:checkpoint", + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "dreamshaper_8.safetensors" + ] + }, + { + "id": 76, + "type": "LoadLorasFromPrompt", + "pos": [ + -283, + 168 + ], + "size": { + "0": 462, + "1": 200 + }, + "flags": {}, + "order": 18, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 140 + }, + { + "name": "clip", + "type": "CLIP", + "link": 203 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 133 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 134 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "STRING", + "type": "STRING", + "links": [ + 135 + ], + "shape": 3, + "slot_index": 2 + } + ], + "title": "Load Loras From Prompt | id:multi_loras_positive_prompt", + "properties": { + "Node name for S&R": "LoadLorasFromPrompt" + }, + "widgets_values": [ + "" + ] + }, + { + "id": 109, + "type": "CLIPSetLastLayer", + "pos": [ + -845.5936890258794, + 460.91404269409134 + ], + "size": { + "0": 315, + "1": 58 + }, + "flags": {}, + "order": 13, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 202 + } + ], + "outputs": [ + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 203, + 204 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "CLIP Set Last Layer | id:clip_skip", + "properties": { + "Node name for S&R": "CLIPSetLastLayer" + }, + "widgets_values": [ + -1 + ] + }, + { + "id": 87, + "type": "LoadImage", + "pos": [ + -2310, + -2190 + ], + "size": { + "0": 306.4342346191406, + "1": 314 + }, + "flags": { + "collapsed": false + }, + "order": 8, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 201 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_image_1", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "ComfyUI_temp_cqoqp_00001_.png", + "image" + ] + }, + { + "id": 94, + "type": "LoadImage", + "pos": [ + -1920, + -1670 + ], + "size": { + "0": 285.6000061035156, + "1": 314 + }, + "flags": { + "collapsed": false + }, + "order": 9, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 166 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_image_2", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + }, + { + "id": 96, + "type": "LoadImage", + "pos": [ + -2060, + -1240 + ], + "size": { + "0": 285.6000061035156, + "1": 314 + }, + "flags": {}, + "order": 10, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 168 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_image_3", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + } + ], + "links": [ + [ + 9, + 8, + 0, + 9, + 0, + "IMAGE" + ], + [ + 15, + 11, + 0, + 13, + 0, + "LATENT" + ], + [ + 17, + 13, + 0, + 12, + 0, + "IMAGE" + ], + [ + 36, + 3, + 0, + 8, + 0, + "LATENT" + ], + [ + 78, + 3, + 0, + 55, + 0, + "LATENT" + ], + [ + 79, + 55, + 0, + 11, + 3, + "LATENT" + ], + [ + 82, + 56, + 0, + 8, + 1, + "VAE" + ], + [ + 83, + 56, + 0, + 13, + 1, + "VAE" + ], + [ + 85, + 56, + 0, + 59, + 1, + "VAE" + ], + [ + 86, + 58, + 0, + 60, + 0, + "IMAGE" + ], + [ + 87, + 60, + 0, + 59, + 0, + "IMAGE" + ], + [ + 92, + 63, + 0, + 3, + 3, + "LATENT" + ], + [ + 94, + 65, + 0, + 3, + 0, + "MODEL" + ], + [ + 95, + 65, + 0, + 11, + 0, + "MODEL" + ], + [ + 100, + 66, + 0, + 6, + 0, + "CLIP" + ], + [ + 133, + 76, + 0, + 65, + 0, + "*" + ], + [ + 134, + 76, + 1, + 66, + 0, + "*" + ], + [ + 135, + 76, + 2, + 6, + 1, + "STRING" + ], + [ + 137, + 78, + 2, + 7, + 1, + "STRING" + ], + [ + 138, + 78, + 1, + 7, + 0, + "CLIP" + ], + [ + 140, + 78, + 0, + 76, + 0, + "MODEL" + ], + [ + 142, + 57, + 0, + 56, + 0, + "*" + ], + [ + 153, + 85, + 0, + 11, + 1, + "CONDITIONING" + ], + [ + 154, + 85, + 0, + 3, + 1, + "CONDITIONING" + ], + [ + 155, + 86, + 0, + 11, + 2, + "CONDITIONING" + ], + [ + 156, + 86, + 0, + 3, + 2, + "CONDITIONING" + ], + [ + 161, + 91, + 0, + 88, + 0, + "IMAGE" + ], + [ + 162, + 91, + 1, + 89, + 0, + "IMAGE" + ], + [ + 163, + 91, + 2, + 90, + 0, + "IMAGE" + ], + [ + 165, + 92, + 0, + 91, + 3, + "IMAGE" + ], + [ + 166, + 94, + 0, + 91, + 4, + "IMAGE" + ], + [ + 167, + 93, + 0, + 91, + 5, + "IMAGE" + ], + [ + 168, + 96, + 0, + 91, + 6, + "IMAGE" + ], + [ + 169, + 95, + 0, + 91, + 7, + "IMAGE" + ], + [ + 171, + 7, + 0, + 91, + 1, + "CONDITIONING" + ], + [ + 174, + 91, + 4, + 86, + 0, + "*" + ], + [ + 190, + 60, + 0, + 104, + 1, + "IMAGE" + ], + [ + 191, + 105, + 0, + 104, + 2, + "CLIP_VISION" + ], + [ + 192, + 16, + 0, + 104, + 0, + "MODEL" + ], + [ + 193, + 104, + 0, + 78, + 0, + "MODEL" + ], + [ + 194, + 104, + 1, + 106, + 1, + "CLIP_VISION_OUTPUT" + ], + [ + 195, + 106, + 0, + 91, + 0, + "CONDITIONING" + ], + [ + 196, + 6, + 0, + 106, + 0, + "CONDITIONING" + ], + [ + 197, + 104, + 1, + 107, + 1, + "CLIP_VISION_OUTPUT" + ], + [ + 198, + 91, + 3, + 107, + 0, + "CONDITIONING" + ], + [ + 199, + 107, + 0, + 85, + 0, + "*" + ], + [ + 200, + 59, + 0, + 63, + 0, + "LATENT" + ], + [ + 201, + 87, + 0, + 91, + 2, + "IMAGE" + ], + [ + 202, + 16, + 1, + 109, + 0, + "CLIP" + ], + [ + 203, + 109, + 0, + 76, + 1, + "CLIP" + ], + [ + 204, + 109, + 0, + 78, + 1, + "CLIP" + ] + ], + "groups": [ + { + "title": "Txt2Img", + "bounding": [ + 442, + -7, + 1211, + 708 + ], + "color": "#a1309b", + "font_size": 24, + "locked": false + }, + { + "title": "Save Intermediate Image", + "bounding": [ + 1225, + 500, + 516, + 196 + ], + "color": "#3f789e", + "font_size": 24, + "locked": false + }, + { + "title": "Hires Fix", + "bounding": [ + 1227, + 17, + 710, + 464 + ], + "color": "#b58b2a", + "font_size": 24, + "locked": false + }, + { + "title": "Save Final Image", + "bounding": [ + 1949, + 31, + 483, + 199 + ], + "color": "#3f789e", + "font_size": 24, + "locked": false + }, + { + "title": "Img2img workflow", + "bounding": [ + 230, + -1475, + 612, + 217 + ], + "color": "#3f789e", + "font_size": 24, + "locked": false + } + ], + "config": {}, + "extra": {}, + "version": 0.4 +} \ No newline at end of file diff --git a/typescripts/comfyui/inpaint_api.json b/typescripts/comfyui/inpaint_api.json new file mode 100644 index 00000000..ba8974cd --- /dev/null +++ b/typescripts/comfyui/inpaint_api.json @@ -0,0 +1,345 @@ +{ + "3": { + "inputs": { + "seed": [ + "141", + 0 + ], + "steps": 20, + "cfg": 7, + "sampler_name": "euler_ancestral", + "scheduler": "karras", + "denoise": 0.71, + "model": [ + "76", + 0 + ], + "positive": [ + "159", + 3 + ], + "negative": [ + "159", + 4 + ], + "latent_image": [ + "160", + 0 + ] + }, + "class_type": "KSampler" + }, + "6": { + "inputs": { + "text": [ + "76", + 2 + ], + "clip": [ + "76", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "text": [ + "78", + 2 + ], + "clip": [ + "78", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "8": { + "inputs": { + "samples": [ + "3", + 0 + ], + "vae": [ + "57", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "9": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": [ + "8", + 0 + ] + }, + "class_type": "SaveImage" + }, + "11": { + "inputs": { + "seed": 4820153955672, + "steps": 20, + "cfg": 8, + "sampler_name": "dpmpp_2m", + "scheduler": "simple", + "denoise": 0.4, + "model": [ + "76", + 0 + ], + "positive": [ + "159", + 3 + ], + "negative": [ + "159", + 4 + ], + "latent_image": [ + "55", + 0 + ] + }, + "class_type": "KSampler" + }, + "12": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": [ + "13", + 0 + ] + }, + "class_type": "SaveImage" + }, + "13": { + "inputs": { + "samples": [ + "11", + 0 + ], + "vae": [ + "57", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "16": { + "inputs": { + "ckpt_name": "aniverse_v15Pruned.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "55": { + "inputs": { + "upscale_method": "nearest-exact", + "scale_by": 2, + "samples": [ + "3", + 0 + ] + }, + "class_type": "LatentUpscaleBy" + }, + "57": { + "inputs": { + "vae_name": "vae-ft-mse-840000-ema-pruned.safetensors" + }, + "class_type": "VAELoader" + }, + "58": { + "inputs": { + "image": "04b380cb676f37e52d5963a0982edd5e.jpg", + "choose file to upload": "image" + }, + "class_type": "LoadImage" + }, + "76": { + "inputs": { + "prompt": "young teen:1.2, (cute girl:1.2)", + "model": [ + "78", + 0 + ], + "clip": [ + "162", + 0 + ] + }, + "class_type": "LoadLorasFromPrompt" + }, + "78": { + "inputs": { + "prompt": "bad hands, text, watermark", + "model": [ + "16", + 0 + ], + "clip": [ + "162", + 0 + ] + }, + "class_type": "LoadLorasFromPrompt" + }, + "83": { + "inputs": { + "image": "pasted/image (4).png", + "choose file to upload": "image" + }, + "class_type": "LoadImage" + }, + "135": { + "inputs": { + "content_mask": "original", + "width": 512, + "height": 768, + "seed": [ + "141", + 0 + ], + "init_image": [ + "58", + 0 + ], + "mask": [ + "83", + 0 + ], + "vae": [ + "57", + 0 + ] + }, + "class_type": "ContentMaskLatent" + }, + "141": { + "inputs": { + "seed": 519416762507340 + }, + "class_type": "APS_Seed" + }, + "155": { + "inputs": { + "width": 512, + "height": 768, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage" + }, + "156": { + "inputs": { + "images": [ + "159", + 0 + ] + }, + "class_type": "PreviewImage" + }, + "157": { + "inputs": { + "images": [ + "159", + 1 + ] + }, + "class_type": "PreviewImage" + }, + "158": { + "inputs": { + "images": [ + "159", + 2 + ] + }, + "class_type": "PreviewImage" + }, + "159": { + "inputs": { + "is_enabled_1": "disable", + "preprocessor_name_1": "OpenposePreprocessor", + "control_net_name_1": "control_lora_rank128_v11p_sd15_openpose_fp16.safetensors", + "strength_1": 1, + "threshold_a_1": 100, + "threshold_b_1": 200, + "start_percent_1": 0, + "end_percent_1": 1, + "resolution_1": 512, + "is_enabled_2": "disable", + "preprocessor_name_2": "OpenposePreprocessor", + "control_net_name_2": "control_lora_rank128_v11p_sd15_openpose_fp16.safetensors", + "strength_2": 1, + "threshold_a_2": 0, + "threshold_b_2": 0, + "start_percent_2": 0, + "end_percent_2": 1, + "resolution_2": 512, + "is_enabled_3": "enable", + "preprocessor_name_3": "InpaintPreprocessor", + "control_net_name_3": "control_lora_rank128_v11p_sd15_inpaint_fp16.safetensors", + "strength_3": 1, + "threshold_a_3": 0, + "threshold_b_3": 0, + "start_percent_3": 0, + "end_percent_3": 1, + "resolution_3": 512, + "positive": [ + "6", + 0 + ], + "negative": [ + "7", + 0 + ], + "image_1": [ + "58", + 0 + ], + "mask_1": [ + "83", + 0 + ], + "image_2": [ + "58", + 0 + ], + "mask_2": [ + "83", + 0 + ], + "image_3": [ + "58", + 0 + ], + "mask_3": [ + "83", + 0 + ] + }, + "class_type": "ControlNetScript" + }, + "160": { + "inputs": { + "amount": 1, + "samples": [ + "135", + 0 + ] + }, + "class_type": "RepeatLatentBatch" + }, + "162": { + "inputs": { + "stop_at_clip_layer": -1, + "clip": [ + "16", + 1 + ] + }, + "class_type": "CLIPSetLastLayer" + } +} \ No newline at end of file diff --git a/typescripts/comfyui/inpaint_workflow.json b/typescripts/comfyui/inpaint_workflow.json new file mode 100644 index 00000000..389bf8c8 --- /dev/null +++ b/typescripts/comfyui/inpaint_workflow.json @@ -0,0 +1,2112 @@ +{ + "last_node_id": 162, + "last_link_id": 313, + "nodes": [ + { + "id": 13, + "type": "VAEDecode", + "pos": [ + 2680, + 940 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 35, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 15 + }, + { + "name": "vae", + "type": "VAE", + "link": 201 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 17 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 66, + "type": "Reroute", + "pos": [ + 160, + 1010 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 23, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 134 + } + ], + "outputs": [ + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 100 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 114, + "type": "Reroute", + "pos": [ + 960, + 1580 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 195 + } + ], + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 196 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 116, + "type": "Reroute", + "pos": [ + 2510, + 780 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 14, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 202, + "slot_index": 0 + } + ], + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 201 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 113, + "type": "Reroute", + "pos": [ + 69, + 1605 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 193 + } + ], + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 195, + 197 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 8, + "type": "VAEDecode", + "pos": [ + 1312, + 1545 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 32, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 278 + }, + { + "name": "vae", + "type": "VAE", + "link": 196 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 9 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 115, + "type": "Reroute", + "pos": [ + 369, + 420 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 11, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 197 + } + ], + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 202, + 247 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 55, + "type": "LatentUpscaleBy", + "pos": [ + 1876, + 1198 + ], + "size": { + "0": 315, + "1": 82 + }, + "flags": {}, + "order": 31, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 78 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 79 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "Upscale Latent By | id:scaler", + "properties": { + "Node name for S&R": "LatentUpscaleBy" + }, + "widgets_values": [ + "nearest-exact", + 2 + ] + }, + { + "id": 65, + "type": "Reroute", + "pos": [ + 190, + 920 + ], + "size": [ + 82, + 26 + ], + "flags": {}, + "order": 22, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 133 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 95, + 263 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 78, + "type": "LoadLorasFromPrompt", + "pos": [ + -2610, + 1250 + ], + "size": { + "0": 462, + "1": 200 + }, + "flags": {}, + "order": 12, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 141 + }, + { + "name": "clip", + "type": "CLIP", + "link": 309 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 140 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 138 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "STRING", + "type": "STRING", + "links": [ + 137 + ], + "shape": 3, + "slot_index": 2 + } + ], + "title": "Load Loras From Prompt | id:multi_loras_negative_prompt", + "properties": { + "Node name for S&R": "LoadLorasFromPrompt" + }, + "widgets_values": [ + "bad hands, text, watermark" + ] + }, + { + "id": 57, + "type": "VAELoader", + "pos": [ + -396, + 1595 + ], + "size": { + "0": 315, + "1": 58 + }, + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 193 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "Load VAE | id:vae", + "properties": { + "Node name for S&R": "VAELoader" + }, + "widgets_values": [ + "vae-ft-mse-840000-ema-pruned.safetensors" + ] + }, + { + "id": 142, + "type": "PreviewImage", + "pos": [ + 2490, + 150 + ], + "size": { + "0": 210, + "1": 246 + }, + "flags": {}, + "order": 20, + "mode": 2, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 261 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 149, + "type": "PreviewImage", + "pos": [ + 2250, + 450 + ], + "size": { + "0": 210, + "1": 246 + }, + "flags": {}, + "order": 21, + "mode": 2, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 277 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 148, + "type": "PreviewImage", + "pos": [ + 2270, + -116 + ], + "size": { + "0": 210, + "1": 246 + }, + "flags": {}, + "order": 19, + "mode": 2, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 276 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 155, + "type": "EmptyLatentImage", + "pos": [ + 1329.5197187499998, + 447.17789685058585 + ], + "size": { + "0": 315, + "1": 106 + }, + "flags": {}, + "order": 1, + "mode": 0, + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 512, + 768, + 1 + ] + }, + { + "id": 7, + "type": "CLIPTextEncode", + "pos": [ + -1760, + 1050 + ], + "size": { + "0": 425.27801513671875, + "1": 180.6060791015625 + }, + "flags": {}, + "order": 17, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 138 + }, + { + "name": "text", + "type": "STRING", + "link": 137, + "widget": { + "name": "text" + } + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 295 + ], + "slot_index": 0 + } + ], + "title": "CLIP Text Encode (Prompt) | id:negative_prompt", + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "bad hands, text, watermark\n" + ] + }, + { + "id": 9, + "type": "SaveImage", + "pos": [ + 1546, + 1609 + ], + "size": { + "0": 277.20001220703125, + "1": 282.4336242675781 + }, + "flags": {}, + "order": 34, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9 + } + ], + "title": "Save Image | id:first_pass_output", + "properties": {}, + "widgets_values": [ + "ComfyUI" + ] + }, + { + "id": 153, + "type": "ImageToMask", + "pos": [ + -720, + -840 + ], + "size": { + "0": 315, + "1": 58 + }, + "flags": {}, + "order": 13, + "mode": 4, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 283 + } + ], + "outputs": [ + { + "name": "MASK", + "type": "MASK", + "links": [], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageToMask" + }, + "widgets_values": [ + "red" + ] + }, + { + "id": 58, + "type": "LoadImage", + "pos": [ + 933, + -569 + ], + "size": { + "0": 315, + "1": 314 + }, + "flags": {}, + "order": 2, + "mode": 0, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 258, + 274 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:init_image", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "04b380cb676f37e52d5963a0982edd5e.jpg", + "image" + ] + }, + { + "id": 3, + "type": "KSampler", + "pos": [ + 757, + 831 + ], + "size": { + "0": 315, + "1": 446 + }, + "flags": {}, + "order": 30, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 263 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 298, + "slot_index": 1 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 299 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 306 + }, + { + "name": "seed", + "type": "INT", + "link": 260, + "widget": { + "name": "seed" + } + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 78, + 278 + ], + "slot_index": 0 + } + ], + "title": "KSampler | id:sampler", + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 63028417688397, + "randomize", + 20, + 7, + "euler_ancestral", + "karras", + 0.71 + ] + }, + { + "id": 12, + "type": "SaveImage", + "pos": [ + 3162, + 1131 + ], + "size": { + "0": 407.53717041015625, + "1": 468.13226318359375 + }, + "flags": {}, + "order": 36, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 17 + } + ], + "title": "Save Image | id:hires_output", + "properties": {}, + "widgets_values": [ + "ComfyUI" + ] + }, + { + "id": 121, + "type": "Reroute", + "pos": [ + 2069, + 410 + ], + "size": [ + 90.4, + 26 + ], + "flags": {}, + "order": 18, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 290 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 305 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + }, + "color": "#223", + "bgcolor": "#335" + }, + { + "id": 160, + "type": "RepeatLatentBatch", + "pos": [ + 1752, + 517 + ], + "size": { + "0": 315, + "1": 58 + }, + "flags": {}, + "order": 24, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 305 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 306 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "Repeat Latent Batch | id:latent_batch", + "properties": { + "Node name for S&R": "RepeatLatentBatch" + }, + "widgets_values": [ + 1 + ] + }, + { + "id": 157, + "type": "PreviewImage", + "pos": [ + -610, + -240 + ], + "size": { + "0": 336, + "1": 246 + }, + "flags": {}, + "order": 28, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 292 + } + ], + "title": "Preview Image | id:preprocessor_output_2", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 156, + "type": "PreviewImage", + "pos": [ + -860, + -240 + ], + "size": { + "0": 336, + "1": 246 + }, + "flags": {}, + "order": 27, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 291 + } + ], + "title": "Preview Image | id:preprocessor_output_1", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 158, + "type": "PreviewImage", + "pos": [ + -330, + -212 + ], + "size": { + "0": 336, + "1": 246 + }, + "flags": {}, + "order": 29, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 293 + } + ], + "title": "Preview Image | id:preprocessor_output_3", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 147, + "type": "ImageScale", + "pos": [ + -1084, + -646 + ], + "size": { + "0": 315, + "1": 130 + }, + "flags": {}, + "order": 7, + "mode": 4, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 274 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 296, + 303, + 307 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageScale" + }, + "widgets_values": [ + "nearest-exact", + 512, + 768, + "disabled" + ] + }, + { + "id": 152, + "type": "ImageScale", + "pos": [ + -1030, + -1050 + ], + "size": { + "0": 315, + "1": 130 + }, + "flags": {}, + "order": 9, + "mode": 4, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 282 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 283, + 302, + 304, + 308 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageScale" + }, + "widgets_values": [ + "nearest-exact", + 512, + 768, + "disabled" + ] + }, + { + "id": 16, + "type": "CheckpointLoaderSimple", + "pos": [ + -3298, + 1138 + ], + "size": { + "0": 315, + "1": 98 + }, + "flags": {}, + "order": 3, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 141 + ], + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 311 + ], + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [], + "slot_index": 2 + } + ], + "title": "Load Checkpoint | id:checkpoint", + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "aniverse_v15Pruned.safetensors" + ] + }, + { + "id": 162, + "type": "CLIPSetLastLayer", + "pos": [ + -2959, + 1098 + ], + "size": { + "0": 315, + "1": 58 + }, + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 311 + } + ], + "outputs": [ + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 309, + 310 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "CLIP Set Last Layer | id:clip_skip", + "properties": { + "Node name for S&R": "CLIPSetLastLayer" + }, + "widgets_values": [ + -1 + ] + }, + { + "id": 6, + "type": "CLIPTextEncode", + "pos": [ + -1740, + 820 + ], + "size": { + "0": 422.84503173828125, + "1": 164.31304931640625 + }, + "flags": {}, + "order": 25, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 100 + }, + { + "name": "text", + "type": "STRING", + "link": 135, + "widget": { + "name": "text" + } + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 294 + ], + "slot_index": 0 + } + ], + "title": "CLIP Text Encode (Prompt) | id:positive_prompt", + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "lora_bright_style girl" + ] + }, + { + "id": 159, + "type": "ControlNetScript", + "pos": [ + -1160, + 70 + ], + "size": { + "0": 506.0240173339844, + "1": 822 + }, + "flags": {}, + "order": 26, + "mode": 0, + "inputs": [ + { + "name": "positive", + "type": "CONDITIONING", + "link": 294 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 295, + "slot_index": 1 + }, + { + "name": "image_1", + "type": "IMAGE", + "link": 296 + }, + { + "name": "mask_1", + "type": "IMAGE", + "link": 304, + "slot_index": 3 + }, + { + "name": "image_2", + "type": "IMAGE", + "link": 303 + }, + { + "name": "mask_2", + "type": "IMAGE", + "link": 302 + }, + { + "name": "image_3", + "type": "IMAGE", + "link": 307 + }, + { + "name": "mask_3", + "type": "IMAGE", + "link": 308 + } + ], + "outputs": [ + { + "name": "preprocessed_image_1", + "type": "IMAGE", + "links": [ + 291 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "preprocessed_image_2", + "type": "IMAGE", + "links": [ + 292 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "preprocessed_image_3", + "type": "IMAGE", + "links": [ + 293 + ], + "shape": 3, + "slot_index": 2 + }, + { + "name": "positive", + "type": "CONDITIONING", + "links": [ + 298, + 312 + ], + "shape": 3, + "slot_index": 3 + }, + { + "name": "negative", + "type": "CONDITIONING", + "links": [ + 299, + 313 + ], + "shape": 3, + "slot_index": 4 + } + ], + "title": "ControlNet Script | id:controlnet_script", + "properties": { + "Node name for S&R": "ControlNetScript" + }, + "widgets_values": [ + "disable", + "OpenposePreprocessor", + "control_lora_rank128_v11p_sd15_openpose_fp16.safetensors", + 1, + 100, + 200, + 0, + 1, + 512, + "disable", + "OpenposePreprocessor", + "control_lora_rank128_v11p_sd15_openpose_fp16.safetensors", + 1, + 0, + 0, + 0, + 1, + 512, + "enable", + "InpaintPreprocessor", + "control_lora_rank128_v11p_sd15_inpaint_fp16.safetensors", + 1, + 0, + 0, + 0, + 1, + 512 + ] + }, + { + "id": 141, + "type": "APS_Seed", + "pos": [ + 711, + 429 + ], + "size": { + "0": 361.20001220703125, + "1": 82 + }, + "flags": {}, + "order": 4, + "mode": 0, + "outputs": [ + { + "name": "s", + "type": "INT", + "links": [ + 259, + 260 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "Auto-Photoshop-SD Seed | id:first_pass_seed", + "properties": { + "Node name for S&R": "APS_Seed" + }, + "widgets_values": [ + 519416762507340, + "fixed" + ] + }, + { + "id": 11, + "type": "KSampler", + "pos": [ + 2227, + 1000 + ], + "size": { + "0": 315, + "1": 474 + }, + "flags": {}, + "order": 33, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 95, + "slot_index": 0 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 312, + "slot_index": 1 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 313, + "slot_index": 2 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 79, + "slot_index": 3 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 15 + ], + "slot_index": 0 + } + ], + "title": "KSampler | id:hires_sampler", + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 4820153955672, + "fixed", + 20, + 8, + "dpmpp_2m", + "simple", + 0.4 + ] + }, + { + "id": 76, + "type": "LoadLorasFromPrompt", + "pos": [ + -2600, + 980 + ], + "size": { + "0": 462, + "1": 200 + }, + "flags": {}, + "order": 16, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 140 + }, + { + "name": "clip", + "type": "CLIP", + "link": 310 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 133 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 134 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "STRING", + "type": "STRING", + "links": [ + 135 + ], + "shape": 3, + "slot_index": 2 + } + ], + "title": "Load Loras From Prompt | id:multi_loras_positive_prompt", + "properties": { + "Node name for S&R": "LoadLorasFromPrompt" + }, + "widgets_values": [ + "young teen:1.2, (cute girl:1.2)" + ] + }, + { + "id": 83, + "type": "LoadImage", + "pos": [ + 422, + -246 + ], + "size": { + "0": 315, + "1": 314 + }, + "flags": {}, + "order": 5, + "mode": 0, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 257, + 282 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": [], + "shape": 3, + "slot_index": 1 + } + ], + "title": "Load Image | id:mask_image", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "pasted/image (4).png", + "image" + ] + }, + { + "id": 135, + "type": "ContentMaskLatent", + "pos": [ + 1312, + 89 + ], + "size": [ + 418.1999816894531, + 214 + ], + "flags": {}, + "order": 15, + "mode": 0, + "inputs": [ + { + "name": "init_image", + "type": "IMAGE", + "link": 258 + }, + { + "name": "mask", + "type": "IMAGE", + "link": 257 + }, + { + "name": "vae", + "type": "VAE", + "link": 247, + "slot_index": 2 + }, + { + "name": "seed", + "type": "INT", + "link": 259, + "widget": { + "name": "seed" + } + } + ], + "outputs": [ + { + "name": "latents", + "type": "LATENT", + "links": [ + 290 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "original_preview", + "type": "IMAGE", + "links": [ + 276 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "latent_noise_preview", + "type": "IMAGE", + "links": [ + 261 + ], + "shape": 3, + "slot_index": 2 + }, + { + "name": "latent_nothing_preview", + "type": "IMAGE", + "links": [ + 277 + ], + "shape": 3, + "slot_index": 3 + } + ], + "title": "Content Mask Latent | id:content_mask_latent", + "properties": { + "Node name for S&R": "ContentMaskLatent" + }, + "widgets_values": [ + "original", + 512, + 768, + 185734188050359, + "randomize" + ] + } + ], + "links": [ + [ + 9, + 8, + 0, + 9, + 0, + "IMAGE" + ], + [ + 15, + 11, + 0, + 13, + 0, + "LATENT" + ], + [ + 17, + 13, + 0, + 12, + 0, + "IMAGE" + ], + [ + 78, + 3, + 0, + 55, + 0, + "LATENT" + ], + [ + 79, + 55, + 0, + 11, + 3, + "LATENT" + ], + [ + 95, + 65, + 0, + 11, + 0, + "MODEL" + ], + [ + 100, + 66, + 0, + 6, + 0, + "CLIP" + ], + [ + 133, + 76, + 0, + 65, + 0, + "*" + ], + [ + 134, + 76, + 1, + 66, + 0, + "*" + ], + [ + 135, + 76, + 2, + 6, + 1, + "STRING" + ], + [ + 137, + 78, + 2, + 7, + 1, + "STRING" + ], + [ + 138, + 78, + 1, + 7, + 0, + "CLIP" + ], + [ + 140, + 78, + 0, + 76, + 0, + "MODEL" + ], + [ + 141, + 16, + 0, + 78, + 0, + "MODEL" + ], + [ + 193, + 57, + 0, + 113, + 0, + "*" + ], + [ + 195, + 113, + 0, + 114, + 0, + "*" + ], + [ + 196, + 114, + 0, + 8, + 1, + "VAE" + ], + [ + 197, + 113, + 0, + 115, + 0, + "*" + ], + [ + 201, + 116, + 0, + 13, + 1, + "VAE" + ], + [ + 202, + 115, + 0, + 116, + 0, + "*" + ], + [ + 247, + 115, + 0, + 135, + 2, + "VAE" + ], + [ + 257, + 83, + 0, + 135, + 1, + "IMAGE" + ], + [ + 258, + 58, + 0, + 135, + 0, + "IMAGE" + ], + [ + 259, + 141, + 0, + 135, + 3, + "INT" + ], + [ + 260, + 141, + 0, + 3, + 4, + "INT" + ], + [ + 261, + 135, + 2, + 142, + 0, + "IMAGE" + ], + [ + 263, + 65, + 0, + 3, + 0, + "MODEL" + ], + [ + 274, + 58, + 0, + 147, + 0, + "IMAGE" + ], + [ + 276, + 135, + 1, + 148, + 0, + "IMAGE" + ], + [ + 277, + 135, + 3, + 149, + 0, + "IMAGE" + ], + [ + 278, + 3, + 0, + 8, + 0, + "LATENT" + ], + [ + 282, + 83, + 0, + 152, + 0, + "IMAGE" + ], + [ + 283, + 152, + 0, + 153, + 0, + "IMAGE" + ], + [ + 290, + 135, + 0, + 121, + 0, + "*" + ], + [ + 291, + 159, + 0, + 156, + 0, + "IMAGE" + ], + [ + 292, + 159, + 1, + 157, + 0, + "IMAGE" + ], + [ + 293, + 159, + 2, + 158, + 0, + "IMAGE" + ], + [ + 294, + 6, + 0, + 159, + 0, + "CONDITIONING" + ], + [ + 295, + 7, + 0, + 159, + 1, + "CONDITIONING" + ], + [ + 296, + 147, + 0, + 159, + 2, + "IMAGE" + ], + [ + 298, + 159, + 3, + 3, + 1, + "CONDITIONING" + ], + [ + 299, + 159, + 4, + 3, + 2, + "CONDITIONING" + ], + [ + 302, + 152, + 0, + 159, + 5, + "IMAGE" + ], + [ + 303, + 147, + 0, + 159, + 4, + "IMAGE" + ], + [ + 304, + 152, + 0, + 159, + 3, + "IMAGE" + ], + [ + 305, + 121, + 0, + 160, + 0, + "LATENT" + ], + [ + 306, + 160, + 0, + 3, + 3, + "LATENT" + ], + [ + 307, + 147, + 0, + 159, + 6, + "IMAGE" + ], + [ + 308, + 152, + 0, + 159, + 7, + "IMAGE" + ], + [ + 309, + 162, + 0, + 78, + 1, + "CLIP" + ], + [ + 310, + 162, + 0, + 76, + 1, + "CLIP" + ], + [ + 311, + 16, + 1, + 162, + 0, + "CLIP" + ], + [ + 312, + 159, + 3, + 11, + 1, + "CONDITIONING" + ], + [ + 313, + 159, + 4, + 11, + 2, + "CONDITIONING" + ] + ], + "groups": [ + { + "title": "Txt2Img", + "bounding": [ + 433, + 692, + 1386, + 1295 + ], + "color": "#a1309b", + "font_size": 24, + "locked": false + }, + { + "title": "Save Intermediate Image", + "bounding": [ + 1283, + 1497, + 529, + 440 + ], + "color": "#3f789e", + "font_size": 24, + "locked": false + }, + { + "title": "Hires Fix", + "bounding": [ + 1864, + 887, + 762, + 544 + ], + "color": "#b58b2a", + "font_size": 24, + "locked": false + }, + { + "title": "Save Final Image", + "bounding": [ + 2671, + 785, + 976, + 856 + ], + "color": "#3f789e", + "font_size": 24, + "locked": false + }, + { + "title": "inpaint workflow", + "bounding": [ + -248, + -966, + 697, + 249 + ], + "color": "#3f789e", + "font_size": 24, + "locked": false + } + ], + "config": {}, + "extra": {}, + "version": 0.4 +} \ No newline at end of file diff --git a/typescripts/comfyui/main_ui.tsx b/typescripts/comfyui/main_ui.tsx new file mode 100644 index 00000000..8aa7bc5e --- /dev/null +++ b/typescripts/comfyui/main_ui.tsx @@ -0,0 +1,570 @@ +import txt2img from './txt2img_workflow.json' +import txt2img_api from './txt2img_api.json' + +import img2img from './img2img_workflow.json' +import img2img_api from './img2img_api.json' + +import inpaint from './inpaint_workflow.json' +import inpaint_api from './inpaint_api.json' + +import vae_settings from '../settings/vae' +import sd_tab_util from '../sd_tab/util' +import comfyui_util from './util' +import util from './util' +import { store } from './util' + +import { base64UrlToBase64, copyJson } from '../util/ts/general' +import { session_store } from '../stores' +import ControlNetStore from '../controlnet/store' +import { setControlDetectMapSrc } from '../controlnet/entry' + +// Function to parse metadata from a title string +function parseMetadata(title: string) { + if (!title) return {} + // Split the title into parts using the "|" character and trim whitespace from each part + + var parts = title.split('|').map((part) => part.trim()) + // Take the last part as the metadata + var metadataPart = parts[parts.length - 1] + // Initialize an empty object to store the key-value pairs + var result: Record = {} + // If there is more than one part, there is metadata to parse + if (parts.length > 1) { + // Split the metadata into pairs using the "," character and trim whitespace from each pair + var pairs = metadataPart.split(',').map((pair) => pair.trim()) + + // For each pair... + for (var i = 0; i < pairs.length; i++) { + // If the pair includes a ":" character, it is a key-value pair + if (pairs[i].includes(':')) { + // Split the pair into a key and a value using the ":" character and trim whitespace + var pair = pairs[i].split(':').map((part) => part.trim()) + + // Add the key-value pair to the result object + result[pair[0]] = pair[1] + } + } + } + + // Return the result object containing the key-value pairs + return result +} + +function getInput(node: any, name: string) { + const input = node.inputs.filter((input: any) => { + return input?.widget?.name === name + })?.[0] + return input +} +function getNode(nodes: any[], node_id: string) { + const node = nodes.filter((node: any) => { + return parseInt(node.id) === parseInt(node_id) + })?.[0] + return node +} +function getNodeByNameId(nodes: any[], node_name_id: string) { + const node = nodes.filter((node: any) => { + const node_metadata = parseMetadata(node.title) + return node_metadata.id === node_name_id + })?.[0] + return node +} + +function getPromptNodeByNameId( + nodes: any[], //nodes from workflow.json + prompt: any, //prompt from api.json + node_name_id: string // name_id I'm using to get access to nodes by their name +) { + const node = getNodeByNameId(nodes, node_name_id) + const prompt_node = node?.id ? prompt[node.id] : {} + + return prompt_node +} +function setInputValue( + nodes: any[], + prompt: any, + node_name_id: string, + input_name: string, + new_value: any +) { + try { + var prompt_node = getPromptNodeByNameId(nodes, prompt, node_name_id) + prompt_node.inputs[input_name] = new_value + } catch (e) { + console.error( + `Node Name ID:\n${node_name_id}\n` + + `Input Name:\n${input_name}\n` + + `New Value:\n${new_value}\n` + + `Prompt Node:\n${prompt_node}\n` + + `Error:\n${e}` + ) + } +} +function getLink(links: any[], link_id: number) { + return links.filter((link: any) => { + return link[0] === link_id + })?.[0] +} + +function getNodesFromLink(link: any) { + return { + from_node: { id: link[1], input_index: link[2] }, + to_node: { id: link[3], input_index: link[4] }, + } +} +function mutePromptNode(nodes: any[], prompt: any, node_name_id: string) { + const node = getNodeByNameId(nodes, node_name_id) + delete prompt[node.id] + return prompt +} +const txt2img_map: Record = { + model: 'checkpoint.ckpt_name', + comfy_clip_skip: 'clip_skip.stop_at_clip_layer', + vae: 'vae.vae_name', + width: 'latent_image.width', + height: 'latent_image.height', + batch_size: 'latent_image.batch_size', + prompt: 'multi_loras_positive_prompt.prompt', + negative_prompt: 'multi_loras_negative_prompt.prompt', + + //sampler node + + seed: 'sampler.seed', + steps: 'sampler.steps', + cfg_scale: 'sampler.cfg', + sampler_index: 'sampler.sampler_name', + // scheduler: 'normal', + // denoising_strength: 'sampler.denoise', // keep it at default value 1.0 + + //hires_node node: + hr_scale: 'scaler.scale_by', + upscale_method: 'nearest_exact', + + hr_seed: 'hires_sampler.seed', + hr_second_pass_steps: 'hires_sampler.steps', + // hr_cfg: 'hires_sampler.cfg', // keep at default value 0.5 + // hr_sampler_name: 'hires_sampler.sampler_name', + // hr_scheduler: 'normal', + hr_denoising_strength: 'hires_sampler.denoise', +} +const controlnet_txt2img_map: Record = { + comfy_input_image: 'controlnet_script.image', //map controlnet_units[unit_index].input_image from base64 string to comfy image filename + comfy_mask: 'controlnet_script.mask', //map controlnet_units[unit_index].mask from base64 string to comfy image filename + comfy_enabled: 'controlnet_script.is_enabled', //map controlnet_units[unit_index].enabled from boolean [true,false] to ['enable', 'disable'] + module: 'controlnet_script.preprocessor_name', + model: 'controlnet_script.control_net_name', + weight: 'controlnet_script.strength', + guidance_start: 'controlnet_script.start_percent', + guidance_end: 'controlnet_script.end_percent', + processor_res: 'controlnet_script.resolution', + threshold_a: 'controlnet_script.threshold_a', + threshold_b: 'controlnet_script.threshold_b', +} +const img2img_map: Record = { + init_image: 'init_image.image', // note: this is not init_images but init_image + model: 'checkpoint.ckpt_name', + comfy_clip_skip: 'clip_skip.stop_at_clip_layer', + vae: 'vae.vae_name', + width: 'init_image_scale.width', + height: 'init_image_scale.height', + batch_size: 'latent_batch.amount', + // prompt: 'positive_prompt.text', + prompt: 'multi_loras_positive_prompt.prompt', + negative_prompt: 'multi_loras_negative_prompt.prompt', + + //sampler node + + seed: 'sampler.seed', + steps: 'sampler.steps', + cfg_scale: 'sampler.cfg', + sampler_index: 'sampler.sampler_name', + // scheduler: 'normal', + denoising_strength: 'sampler.denoise', + + //hires_node node: + hr_scale: 'scaler.scale_by', + upscale_method: 'nearest_exact', + + hr_seed: 'hires_sampler.seed', + hr_second_pass_steps: 'hires_sampler.steps', + // hr_cfg: 'hires_sampler.cfg', // keep at default value 0.5 + // hr_sampler_name: 'hires_sampler.sampler_name', + // hr_scheduler: 'normal', + hr_denoising_strength: 'hires_sampler.denoise', +} +const inpaint_map: Record = { + init_image: 'init_image.image', // note: this is not init_images but init_image + comfy_mask: 'mask_image.image', + model: 'checkpoint.ckpt_name', + comfy_clip_skip: 'clip_skip.stop_at_clip_layer', + vae: 'vae.vae_name', + width: 'content_mask_latent.width', + height: 'content_mask_latent.height', + batch_size: 'latent_batch.amount', + // prompt: 'positive_prompt.text', + prompt: 'multi_loras_positive_prompt.prompt', + negative_prompt: 'multi_loras_negative_prompt.prompt', + + comfy_content_mask: 'content_mask_latent.content_mask', + + //sampler node + + seed: 'first_pass_seed.seed', + steps: 'sampler.steps', + cfg_scale: 'sampler.cfg', + sampler_index: 'sampler.sampler_name', + // scheduler: 'normal', + denoising_strength: 'sampler.denoise', + + //hires_node node: + hr_scale: 'scaler.scale_by', + upscale_method: 'nearest_exact', + + hr_seed: 'hires_sampler.seed', + hr_second_pass_steps: 'hires_sampler.steps', + // hr_cfg: 'hires_sampler.cfg', // keep at default value 0.5 + // hr_sampler_name: 'hires_sampler.sampler_name', + // hr_scheduler: 'normal', + hr_denoising_strength: 'hires_sampler.denoise', +} +async function reuseOrUploadComfyImage( + base64: string, + all_uploaded_images: Record +) { + let image_name: string = '' + if (all_uploaded_images[base64]) { + image_name = all_uploaded_images[base64] + } else { + const new_loaded_image = await util.uploadImage(false, base64) + console.log('new_loaded_image: ', new_loaded_image) + if (new_loaded_image) { + store.data.uploaded_images_list = [ + ...store.data.uploaded_images_list, + new_loaded_image.name, + ] + image_name = new_loaded_image.name + all_uploaded_images[base64] = new_loaded_image.name + } + } + return image_name +} +async function addMissingSettings(plugin_settings: Record) { + plugin_settings['vae'] = vae_settings.store.data.current_vae + plugin_settings['model'] = sd_tab_util.store.data.selected_model + plugin_settings['hr_denoising_strength'] = + sd_tab_util.store.data.hr_denoising_strength + plugin_settings['hr_sampler_name'] = sd_tab_util.store.data.sampler_name // use the same sampler for the first and second pass (hires) upscale sampling steps + plugin_settings['comfy_clip_skip'] = -1 * plugin_settings['clip_skip'] + if ('init_images' in plugin_settings) { + const base64 = plugin_settings['init_images'][0] + + plugin_settings['init_image'] = await reuseOrUploadComfyImage( + base64, + store.data.base64_to_uploaded_images_names + ) + } + if ('mask' in plugin_settings) { + const base64 = plugin_settings['mask'] + + plugin_settings['comfy_mask'] = await reuseOrUploadComfyImage( + base64, + store.data.base64_to_uploaded_images_names + ) + } + + //calculate positive random seed if seed is -1 + const random_seed: bigint = util.getRandomBigIntApprox( + 0n, + 18446744073709552000n + ) + + plugin_settings['seed'] = + parseInt(plugin_settings['seed']) === -1 + ? random_seed.toString() + : plugin_settings['seed'] // use the same as the main seed + + session_store.data.last_seed = plugin_settings['seed'] + plugin_settings['hr_seed'] = plugin_settings['seed'] + + return plugin_settings +} + +async function addMissingControlnetSettings( + plugin_settings: Record +) { + plugin_settings['disableControlNetTab'] = + ControlNetStore.disableControlNetTab + for (const unit of plugin_settings['controlnet_units']) { + unit['comfy_enabled'] = + !plugin_settings['disableControlNetTab'] && unit.enabled + ? 'enable' + : 'disable' + + unit['comfy_input_image'] = '' + unit['comfy_mask'] = '' + if ('input_image' in unit && unit['input_image'] !== '') { + const base64 = unit['input_image'] + unit['comfy_input_image'] = await reuseOrUploadComfyImage( + base64, + store.data.base64_to_uploaded_images_names + ) + } + if ('mask' in unit && unit['mask'] !== '') { + //if mask have been set manually + const base64 = unit['mask'] + + unit['comfy_mask'] = await reuseOrUploadComfyImage( + base64, + store.data.base64_to_uploaded_images_names + ) + } else if ('comfy_mask' in plugin_settings) { + // use the mask from the main ui (inpaint and outpaint mode) + + unit['comfy_mask'] = plugin_settings['comfy_mask'] + } + + //set model and module to 'None' if no item has been selection, so comfyui won't through an error + unit['model'] = + unit['model'] === '' || unit['comfy_enabled'] === 'disable' + ? 'None' + : unit['model'] + unit['module'] = + unit['module'] === '' || unit['comfy_enabled'] === 'disable' + ? 'None' + : unit['module'] + } + return plugin_settings +} +async function mapPluginSettingsToComfyuiPrompt( + nodes: any[], + prompt: any, + plugin_settings: any, + mode_map: any +) { + try { + // const plugin_param = 'steps' + plugin_settings = await addMissingSettings(plugin_settings) + function mapPluginInputToComfyInput( + plugin_settings: Record, + plugin_param: string, + node_name_id: string, + input_name: string + ) { + if (plugin_param in plugin_settings) { + setInputValue( + nodes, + prompt, + node_name_id, + input_name, + plugin_settings[plugin_param] + ) + } + } + + Object.keys(mode_map).forEach((plugin_param: string) => { + const [node_name_id, input_name] = mode_map[plugin_param].split('.') + mapPluginInputToComfyInput( + plugin_settings, + plugin_param, + node_name_id, + input_name + ) + }) + + plugin_settings = await addMissingControlnetSettings(plugin_settings) + for (let i = 0; i < 3; ++i) { + const unit = plugin_settings['controlnet_units'][i] + // one for each controlnet unit + Object.keys(controlnet_txt2img_map).forEach( + (plugin_param: string) => { + let [node_name_id, input_name] = + controlnet_txt2img_map[plugin_param].split('.') + // if ( + // node_name_id === 'controlnet_image' || + // node_name_id === 'controlnet_mask' + // ) { + // // the input images and masks are each in separate nodes + // node_name_id = `${node_name_id}_${i + 1}` //ex: 'controlnet_image.image' -> controlnet_image_1.image + // } else { + // //all other inputs present in the controlnet script + // input_name = `${input_name}_${i + 1}` //ex: preprocessor_name -> preprocessor_name_1 + // } + input_name = `${input_name}_${i + 1}` //ex: preprocessor_name -> preprocessor_name_1 + + mapPluginInputToComfyInput( + unit, + plugin_param, + node_name_id, + input_name + ) + } + ) + } + } catch (e) { + console.error(e) + } + return prompt +} + +async function generateComfyMode( + nodes: any[], + api_prompt: Record, + plugin_settings: Record, + mode_map: Record +): Promise<{ image_base64_list: string[]; image_url_list: string[] }> { + let image_url_list: string[] = [] + let image_base64_list: string[] = [] + try { + // const controlnet_settings = + // mapPluginSettingsToControlNet(plugin_settings) + + // console.log('controlnet_settings:', controlnet_settings) + const prompt = await mapPluginSettingsToComfyuiPrompt( + nodes, + copyJson(api_prompt), + plugin_settings, + mode_map + ) + const final_prompt = copyJson(prompt) + if (!plugin_settings['enable_hr']) { + //get node_id + const hire_output_node = getNodeByNameId(nodes, 'hires_output') + + delete final_prompt[hire_output_node.id] + } + + const separated_output_node_ids: string[] = [] + const node_id_to_controlnet_unit_index: Record = {} + for (const [index, unit] of plugin_settings[ + 'controlnet_units' + ].entries()) { + const node_name_id = `preprocessor_output_${index + 1}` + const node = getNodeByNameId(nodes, node_name_id) + const node_id = node.id.toString() + node_id_to_controlnet_unit_index[node_id] = index + + if (unit['comfy_enabled'] === 'disable') { + mutePromptNode(nodes, final_prompt, node_name_id) + } else if (unit['comfy_enabled'] === 'enable') { + separated_output_node_ids.push(node_id) + } + } + // for (let i = 0; i < 3; ++i) { + // const unit = plugin_settings['controlnet_units'][i] + // if (unit['input_image'] === '') { + // mutePromptNode(nodes, final_prompt, `controlnet_image_${i + 1}`) + // } + // if (unit['mask'] === '') { + // mutePromptNode(nodes, final_prompt, `controlnet_mask_${i + 1}`) + // } + // } + console.log('final_prompt: ', final_prompt) + const { outputs, separated_outputs } = + await comfyui_util.postPromptAndGetBase64JsonResult( + final_prompt, + separated_output_node_ids + ) + + if (outputs) { + image_url_list = Object.values(outputs).flat() + image_base64_list = image_url_list.map((image_url) => { + return base64UrlToBase64(image_url) + }) + } + if (separated_outputs) { + Object.entries(separated_outputs).forEach(([node_id, images]) => { + const controlnet_unit_index = + node_id_to_controlnet_unit_index[node_id] + setControlDetectMapSrc( + base64UrlToBase64(images[0]), + controlnet_unit_index + ) + }) + } + } catch (e) { + console.error(e) + } + return { image_base64_list, image_url_list } +} +async function generateComfyTxt2Img( + plugin_settings: any +): Promise<{ image_base64_list: string[]; image_url_list: string[] }> { + return generateComfyMode( + txt2img.nodes, + txt2img_api, + plugin_settings, + txt2img_map + ) +} +async function generateComfyImg2Img( + plugin_settings: any +): Promise<{ image_base64_list: string[]; image_url_list: string[] }> { + return generateComfyMode( + img2img.nodes, + img2img_api, + plugin_settings, + img2img_map + ) +} +async function generateComfyInpaint( + plugin_settings: any +): Promise<{ image_base64_list: string[]; image_url_list: string[] }> { + if ('inpainting_fill' in plugin_settings) { + const index = plugin_settings['inpainting_fill'] + const content_mask_option = [ + 'fill', + 'original', + 'latent_noise', + 'latent_nothing', + ] + plugin_settings['comfy_content_mask'] = content_mask_option[index] + + // const content_mask = [ + // ['fill', ''], + // ['original', 'content_mask_original_output'], + // ['latent_noise', 'content_mask_latent_noise_output'], + // ['latent_nothing', 'content_mask_latent_nothing_output'], + // ] + + // const comfy_node_name_id = content_mask[index][1] + // const content_mask_node = getNodeByNameId( + // inpaint.nodes, + // comfy_node_name_id + // ) + + // if (index > 0 && index <= 3 && content_mask_node) { + // plugin_settings['comfy_content_mask'] = [ + // content_mask_node.id.toString(), + // 0, + // ] + // } + } + return generateComfyMode( + inpaint.nodes, + inpaint_api, + plugin_settings, + inpaint_map + ) +} + +export default { + parseMetadata, + getNode, + getInput, + getLink, + getNodesFromLink, + getNodeByNameId, + mapPluginSettingsToComfyuiPrompt, + getPromptNodeByNameId, + setInputValue, + addMissingSettings, + generateComfyTxt2Img, + generateComfyImg2Img, + generateComfyInpaint, + txt2img, + txt2img_api, + img2img, + img2img_api, + inpaint, + inpaint_api, +} diff --git a/typescripts/comfyui/native_workflows/IPAdapter_simple_api.json b/typescripts/comfyui/native_workflows/IPAdapter_simple_api.json new file mode 100644 index 00000000..eefb2de8 --- /dev/null +++ b/typescripts/comfyui/native_workflows/IPAdapter_simple_api.json @@ -0,0 +1,167 @@ +{ + "8": { + "inputs": { + "samples": [ + "50", + 0 + ], + "vae": [ + "32", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "14": { + "inputs": { + "ckpt_name": "dreamshaper_8.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "32": { + "inputs": { + "vae_name": "vae-ft-mse-840000-ema-pruned.safetensors" + }, + "class_type": "VAELoader" + }, + "50": { + "inputs": { + "add_noise": "enable", + "noise_seed": 538408362410054, + "steps": 30, + "cfg": 7, + "sampler_name": "dpmpp_2m", + "scheduler": "karras", + "start_at_step": 0, + "end_at_step": 10000, + "return_with_leftover_noise": "disable", + "model": [ + "210", + 0 + ], + "positive": [ + "211", + 0 + ], + "negative": [ + "212", + 0 + ], + "latent_image": [ + "95", + 0 + ] + }, + "class_type": "KSamplerAdvanced" + }, + "95": { + "inputs": { + "width": 512, + "height": 512, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage" + }, + "192": { + "inputs": { + "images": [ + "8", + 0 + ] + }, + "class_type": "PreviewImage" + }, + "204": { + "inputs": { + "weight": 0.5, + "noise": 0.33, + "weight_type": "original", + "start_at": 0, + "end_at": 1, + "ipadapter": [ + "205", + 0 + ], + "clip_vision": [ + "206", + 0 + ], + "image": [ + "207", + 0 + ], + "model": [ + "14", + 0 + ] + }, + "class_type": "IPAdapterApply" + }, + "205": { + "inputs": { + "ipadapter_file": "ip-adapter-plus_sd15.bin" + }, + "class_type": "IPAdapterModelLoader" + }, + "206": { + "inputs": { + "clip_name": "model.safetensors" + }, + "class_type": "CLIPVisionLoader" + }, + "207": { + "inputs": { + "interpolation": "LANCZOS", + "crop_position": "top", + "sharpening": 0.15, + "image": [ + "209", + 0 + ] + }, + "class_type": "PrepImageForClipVision" + }, + "209": { + "inputs": { + "image": "ComfyUI_temp_cqoqp_00001_ (1).png", + "choose file to upload": "image" + }, + "class_type": "LoadImage" + }, + "210": { + "inputs": { + "block_number": 3, + "downscale_factor": 1.5, + "start_percent": 0, + "end_percent": 0.45, + "downscale_after_skip": true, + "downscale_method": "bicubic", + "upscale_method": "bicubic", + "model": [ + "204", + 0 + ] + }, + "class_type": "PatchModelAddDownscale" + }, + "211": { + "inputs": { + "text": "", + "clip": [ + "14", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "212": { + "inputs": { + "text": "blurry, low quality", + "clip": [ + "14", + 1 + ] + }, + "class_type": "CLIPTextEncode" + } +} \ No newline at end of file diff --git a/typescripts/comfyui/native_workflows/IPAdapter_weighted_api.json b/typescripts/comfyui/native_workflows/IPAdapter_weighted_api.json new file mode 100644 index 00000000..91bc53ab --- /dev/null +++ b/typescripts/comfyui/native_workflows/IPAdapter_weighted_api.json @@ -0,0 +1,180 @@ +{ + "1": { + "inputs": { + "ckpt_name": "dreamshaper_8.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "2": { + "inputs": { + "vae_name": "vae-ft-mse-840000-ema-pruned.safetensors" + }, + "class_type": "VAELoader" + }, + "3": { + "inputs": { + "ipadapter_file": "ip-adapter-plus_sd15.bin" + }, + "class_type": "IPAdapterModelLoader" + }, + "4": { + "inputs": { + "clip_name": "model.safetensors" + }, + "class_type": "CLIPVisionLoader" + }, + "6": { + "inputs": { + "image": "ComfyUI_temp_cqoqp_00001_ (1).png", + "choose file to upload": "image" + }, + "class_type": "LoadImage" + }, + "7": { + "inputs": { + "text": "beautiful renaissance girl, detailed", + "clip": [ + "1", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "8": { + "inputs": { + "text": "blurry, horror", + "clip": [ + "1", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "9": { + "inputs": { + "seed": 857545940756658, + "steps": 35, + "cfg": 5, + "sampler_name": "ddim", + "scheduler": "ddim_uniform", + "denoise": 1, + "model": [ + "13", + 0 + ], + "positive": [ + "7", + 0 + ], + "negative": [ + "8", + 0 + ], + "latent_image": [ + "10", + 0 + ] + }, + "class_type": "KSampler" + }, + "10": { + "inputs": { + "width": 512, + "height": 512, + "batch_size": 4 + }, + "class_type": "EmptyLatentImage" + }, + "11": { + "inputs": { + "samples": [ + "9", + 0 + ], + "vae": [ + "2", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "12": { + "inputs": { + "filename_prefix": "IPAdapter", + "images": [ + "11", + 0 + ] + }, + "class_type": "SaveImage" + }, + "13": { + "inputs": { + "weight": 1, + "weight_type": "original", + "start_at": 0, + "end_at": 1, + "ipadapter": [ + "3", + 0 + ], + "embeds": [ + "14", + 0 + ], + "model": [ + "1", + 0 + ] + }, + "class_type": "IPAdapterApplyEncoded" + }, + "14": { + "inputs": { + "ipadapter_plus": true, + "noise": 0.31, + "weight_1": [ + "16", + 0 + ], + "weight_2": [ + "17", + 0 + ], + "weight_3": 1, + "weight_4": 1, + "clip_vision": [ + "4", + 0 + ], + "image_1": [ + "15", + 0 + ], + "image_2": [ + "6", + 0 + ] + }, + "class_type": "IPAdapterEncoder" + }, + "15": { + "inputs": { + "image": "animation-deku-my-hero-academia.jpg", + "choose file to upload": "image" + }, + "class_type": "LoadImage" + }, + "16": { + "inputs": { + "Value": 1 + }, + "class_type": "Float" + }, + "17": { + "inputs": { + "Value": 0.38 + }, + "class_type": "Float" + } +} \ No newline at end of file diff --git a/typescripts/comfyui/native_workflows/animatediff_lcm_api.json b/typescripts/comfyui/native_workflows/animatediff_lcm_api.json new file mode 100644 index 00000000..5c9b070a --- /dev/null +++ b/typescripts/comfyui/native_workflows/animatediff_lcm_api.json @@ -0,0 +1,182 @@ +{ + "2": { + "inputs": { + "vae_name": "vae-ft-mse-840000-ema-pruned.safetensors" + }, + "class_type": "VAELoader" + }, + "4": { + "inputs": { + "stop_at_clip_layer": -1, + "clip": [ + "32", + 1 + ] + }, + "class_type": "CLIPSetLastLayer" + }, + "6": { + "inputs": { + "text": "embedding:BadDream, ", + "clip": [ + "4", + 0 + ] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "seed": 888888891, + "steps": 8, + "cfg": 1.5, + "sampler_name": "lcm", + "scheduler": "sgm_uniform", + "denoise": 1, + "model": [ + "36", + 0 + ], + "positive": [ + "38", + 0 + ], + "negative": [ + "6", + 0 + ], + "latent_image": [ + "9", + 0 + ] + }, + "class_type": "KSampler" + }, + "9": { + "inputs": { + "width": 512, + "height": 512, + "batch_size": 110 + }, + "class_type": "EmptyLatentImage" + }, + "10": { + "inputs": { + "samples": [ + "7", + 0 + ], + "vae": [ + "2", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "32": { + "inputs": { + "ckpt_name": "dreamshaper_8.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "33": { + "inputs": { + "context_length": 16, + "context_stride": 1, + "context_overlap": 4, + "context_schedule": "uniform", + "closed_loop": false + }, + "class_type": "ADE_AnimateDiffUniformContextOptions" + }, + "36": { + "inputs": { + "model_name": "mm_sd_v15_v2.ckpt", + "beta_schedule": "sqrt_linear (AnimateDiff)", + "motion_scale": 1, + "apply_v2_models_properly": false, + "model": [ + "42", + 0 + ], + "context_options": [ + "33", + 0 + ] + }, + "class_type": "ADE_AnimateDiffLoaderWithContext" + }, + "37": { + "inputs": { + "frame_rate": 8, + "loop_count": 0, + "filename_prefix": "aaa_readme", + "format": "image/gif", + "pingpong": false, + "save_image": true, + "crf": 20, + "save_metadata": true, + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "filename": "aaa_readme_00024.gif", + "subfolder": "", + "type": "output", + "format": "image/gif" + } + }, + "images": [ + "10", + 0 + ] + }, + "class_type": "VHS_VideoCombine" + }, + "38": { + "inputs": { + "text": "\"0\" : \"Spring, flowers, smile\",\n\"20\" : \"Spring, flowers, smile\",\n\"30\" : \"Summer, sun, happy, windy\",\n\"50\" : \"Summer, sun, happy, windy\",\n\"60\" : \"Autumn, yellow leaves, laugh\",\n\"80\" : \"Autumn, yellow leaves, laugh\",\n\"90\" : \"Winter, wind, snow, smile, seductive\",\n\"110\" : \"Winter, wind snow, smile, seductive\"", + "max_frames": 110, + "print_output": false, + "pre_text": "25 year old woman, t-shirt", + "app_text": "", + "start_frame": 0, + "pw_a": 0, + "pw_b": 0, + "pw_c": 0, + "pw_d": 0, + "clip": [ + "4", + 0 + ] + }, + "class_type": "BatchPromptSchedule" + }, + "41": { + "inputs": { + "lora_name": "lcm_lora_sd15.safetensors", + "strength_model": 1, + "strength_clip": 1, + "model": [ + "32", + 0 + ], + "clip": [ + "32", + 1 + ] + }, + "class_type": "LoraLoader" + }, + "42": { + "inputs": { + "sampling": "lcm", + "zsnr": false, + "model": [ + "41", + 0 + ] + }, + "class_type": "ModelSamplingDiscrete" + } +} \ No newline at end of file diff --git a/typescripts/comfyui/native_workflows/animatediff_simple_api.json b/typescripts/comfyui/native_workflows/animatediff_simple_api.json new file mode 100644 index 00000000..633dbac8 --- /dev/null +++ b/typescripts/comfyui/native_workflows/animatediff_simple_api.json @@ -0,0 +1,141 @@ +{ + "2": { + "inputs": { + "vae_name": "vae-ft-mse-840000-ema-pruned.safetensors" + }, + "class_type": "VAELoader" + }, + "3": { + "inputs": { + "text": "girl astronaut walking on the moon. ", + "clip": [ + "4", + 0 + ] + }, + "class_type": "CLIPTextEncode" + }, + "4": { + "inputs": { + "stop_at_clip_layer": -2, + "clip": [ + "32", + 1 + ] + }, + "class_type": "CLIPSetLastLayer" + }, + "6": { + "inputs": { + "text": "(worst quality, low quality: 1.4)", + "clip": [ + "4", + 0 + ] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "seed": 888888889, + "steps": 20, + "cfg": 8, + "sampler_name": "euler", + "scheduler": "normal", + "denoise": 1, + "model": [ + "27", + 0 + ], + "positive": [ + "3", + 0 + ], + "negative": [ + "6", + 0 + ], + "latent_image": [ + "9", + 0 + ] + }, + "class_type": "KSampler" + }, + "9": { + "inputs": { + "width": 512, + "height": 512, + "batch_size": 16 + }, + "class_type": "EmptyLatentImage" + }, + "10": { + "inputs": { + "samples": [ + "7", + 0 + ], + "vae": [ + "2", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "27": { + "inputs": { + "model_name": "mm_sd_v14.ckpt", + "beta_schedule": "sqrt_linear (AnimateDiff)", + "motion_scale": 1, + "apply_v2_models_properly": false, + "model": [ + "32", + 0 + ] + }, + "class_type": "ADE_AnimateDiffLoaderWithContext" + }, + "32": { + "inputs": { + "ckpt_name": "cardosAnime_v20.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "35": { + "inputs": { + "frame_rate": 8, + "loop_count": 0, + "filename_prefix": "aaa_readme", + "format": "image/gif", + "pingpong": false, + "save_image": true, + "crf": 20, + "save_metadata": false, + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "filename": "aaa_readme_00022.gif", + "subfolder": "", + "type": "output", + "format": "image/gif" + } + }, + "images": [ + "10", + 0 + ] + }, + "class_type": "VHS_VideoCombine" + }, + "37": { + "inputs": { + "images": [ + "10", + 0 + ] + }, + "class_type": "PreviewImage" + } +} \ No newline at end of file diff --git a/typescripts/comfyui/native_workflows/lcm_txt2img.json b/typescripts/comfyui/native_workflows/lcm_txt2img.json new file mode 100644 index 00000000..ece05131 --- /dev/null +++ b/typescripts/comfyui/native_workflows/lcm_txt2img.json @@ -0,0 +1,24 @@ +{ + "43": { + "inputs": { + "seed": 927413224602369, + "steps": 4, + "cfg": 8, + "height": 512, + "width": 512, + "num_images": 4, + "use_fp16": true, + "positive_prompt": "cute cat" + }, + "class_type": "LCM_Sampler" + }, + "44": { + "inputs": { + "images": [ + "43", + 0 + ] + }, + "class_type": "PreviewImage" + } +} \ No newline at end of file diff --git a/typescripts/comfyui/native_workflows/zoom_out_api.json b/typescripts/comfyui/native_workflows/zoom_out_api.json new file mode 100644 index 00000000..08431105 --- /dev/null +++ b/typescripts/comfyui/native_workflows/zoom_out_api.json @@ -0,0 +1,333 @@ +{ + "1": { + "inputs": { + "image": "000662ed61a84c86fc8a3fe69d38a6e2 (1).jpg", + "choose file to upload": "image" + }, + "class_type": "LoadImage" + }, + "2": { + "inputs": { + "left": 112, + "top": 112, + "right": 104, + "bottom": 208, + "feathering": 20, + "image": [ + "50", + 0 + ] + }, + "class_type": "ImagePadForOutpaint" + }, + "3": { + "inputs": { + "images": [ + "2", + 0 + ] + }, + "class_type": "PreviewImage" + }, + "5": { + "inputs": { + "width": [ + "12", + 2 + ], + "height": [ + "12", + 1 + ], + "batch_size": 1 + }, + "class_type": "EmptyLatentImage" + }, + "6": { + "inputs": { + "pixels": [ + "2", + 0 + ], + "vae": [ + "7", + 0 + ] + }, + "class_type": "VAEEncode" + }, + "7": { + "inputs": { + "vae_name": "vae-ft-mse-840000-ema-pruned.safetensors" + }, + "class_type": "VAELoader" + }, + "12": { + "inputs": { + "value": [ + "2", + 0 + ] + }, + "class_type": "ImpactImageInfo" + }, + "14": { + "inputs": { + "x": 0, + "y": 0, + "resize_source": true, + "destination": [ + "6", + 0 + ], + "source": [ + "5", + 0 + ], + "mask": [ + "2", + 1 + ] + }, + "class_type": "LatentCompositeMasked" + }, + "15": { + "inputs": { + "samples": [ + "14", + 0 + ], + "vae": [ + "7", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "19": { + "inputs": { + "images": [ + "15", + 0 + ] + }, + "class_type": "PreviewImage" + }, + "20": { + "inputs": { + "strength": 1, + "conditioning": [ + "27", + 4 + ], + "control_net": [ + "21", + 0 + ], + "image": [ + "47", + 0 + ] + }, + "class_type": "ControlNetApply" + }, + "21": { + "inputs": { + "control_net_name": "control_v11p_sd15_inpaint_fp16.safetensors" + }, + "class_type": "ControlNetLoader" + }, + "22": { + "inputs": { + "ckpt_name": "aniverse_v15Pruned.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "23": { + "inputs": { + "model": [ + "22", + 0 + ], + "clip": [ + "22", + 1 + ], + "vae": [ + "22", + 2 + ], + "positive": [ + "24", + 0 + ], + "negative": [ + "25", + 0 + ] + }, + "class_type": "ToBasicPipe" + }, + "24": { + "inputs": { + "text": "", + "clip": [ + "22", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "25": { + "inputs": { + "text": "nsfw", + "clip": [ + "22", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "27": { + "inputs": { + "basic_pipe": [ + "23", + 0 + ] + }, + "class_type": "FromBasicPipe_v2" + }, + "29": { + "inputs": { + "basic_pipe": [ + "27", + 0 + ], + "positive": [ + "20", + 0 + ] + }, + "class_type": "EditBasicPipe" + }, + "30": { + "inputs": { + "seed": 949895177872699, + "steps": 20, + "cfg": 8, + "sampler_name": "euler", + "scheduler": "normal", + "denoise": 1, + "basic_pipe": [ + "29", + 0 + ], + "latent_image": [ + "62", + 0 + ] + }, + "class_type": "ImpactKSamplerBasicPipe" + }, + "31": { + "inputs": { + "samples": [ + "30", + 1 + ], + "vae": [ + "30", + 2 + ] + }, + "class_type": "VAEDecode" + }, + "32": { + "inputs": { + "images": [ + "31", + 0 + ] + }, + "class_type": "PreviewImage" + }, + "33": { + "inputs": { + "samples": [ + "14", + 0 + ], + "mask": [ + "2", + 1 + ] + }, + "class_type": "SetLatentNoiseMask" + }, + "47": { + "inputs": { + "image": [ + "2", + 0 + ], + "mask": [ + "2", + 1 + ] + }, + "class_type": "InpaintPreprocessor" + }, + "50": { + "inputs": { + "upscale_method": "nearest-exact", + "scale_by": 0.3, + "image": [ + "1", + 0 + ] + }, + "class_type": "ImageScaleBy" + }, + "52": { + "inputs": { + "clip_vision": [ + "53", + 0 + ], + "image": [ + "1", + 0 + ] + }, + "class_type": "CLIPVisionEncode" + }, + "53": { + "inputs": { + "clip_name": "SD1.5/pytorch_model.bin" + }, + "class_type": "CLIPVisionLoader" + }, + "56": { + "inputs": { + "pixels": [ + "2", + 0 + ], + "vae": [ + "7", + 0 + ] + }, + "class_type": "VAEEncode" + }, + "62": { + "inputs": { + "amount": 2, + "samples": [ + "33", + 0 + ] + }, + "class_type": "RepeatLatentBatch" + } +} \ No newline at end of file diff --git a/typescripts/comfyui/txt2img_api.json b/typescripts/comfyui/txt2img_api.json new file mode 100644 index 00000000..2fa7b5c6 --- /dev/null +++ b/typescripts/comfyui/txt2img_api.json @@ -0,0 +1,264 @@ +{ + "3": { + "inputs": { + "seed": 945794611996037, + "steps": 12, + "cfg": 8, + "sampler_name": "dpmpp_sde", + "scheduler": "normal", + "denoise": 1, + "model": [ + "58", + 0 + ], + "positive": [ + "81", + 3 + ], + "negative": [ + "81", + 4 + ], + "latent_image": [ + "5", + 0 + ] + }, + "class_type": "KSampler" + }, + "5": { + "inputs": { + "width": 512, + "height": 512, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage" + }, + "6": { + "inputs": { + "text": [ + "58", + 2 + ], + "clip": [ + "58", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "text": [ + "60", + 2 + ], + "clip": [ + "60", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "8": { + "inputs": { + "samples": [ + "3", + 0 + ], + "vae": [ + "57", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "9": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": [ + "8", + 0 + ] + }, + "class_type": "SaveImage" + }, + "11": { + "inputs": { + "seed": 702670332387358, + "steps": 14, + "cfg": 8, + "sampler_name": "dpmpp_2m", + "scheduler": "simple", + "denoise": 0.5, + "model": [ + "58", + 0 + ], + "positive": [ + "81", + 3 + ], + "negative": [ + "81", + 4 + ], + "latent_image": [ + "55", + 0 + ] + }, + "class_type": "KSampler" + }, + "12": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": [ + "13", + 0 + ] + }, + "class_type": "SaveImage" + }, + "13": { + "inputs": { + "samples": [ + "11", + 0 + ], + "vae": [ + "57", + 0 + ] + }, + "class_type": "VAEDecode" + }, + "16": { + "inputs": { + "ckpt_name": "dreamshaper_8.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "55": { + "inputs": { + "upscale_method": "nearest-exact", + "scale_by": 2, + "samples": [ + "3", + 0 + ] + }, + "class_type": "LatentUpscaleBy" + }, + "57": { + "inputs": { + "vae_name": "vae-ft-mse-840000-ema-pruned.safetensors" + }, + "class_type": "VAELoader" + }, + "58": { + "inputs": { + "prompt": " lora_delta girl lora_bright_style girl masterpiece HDR victorian portrait painting of woman, blonde hair, mountain nature, blue sky", + "model": [ + "60", + 0 + ], + "clip": [ + "98", + 0 + ] + }, + "class_type": "LoadLorasFromPrompt" + }, + "60": { + "inputs": { + "prompt": "bad hands, text, watermark", + "model": [ + "16", + 0 + ], + "clip": [ + "98", + 0 + ] + }, + "class_type": "LoadLorasFromPrompt" + }, + "81": { + "inputs": { + "is_enabled_1": "disable", + "preprocessor_name_1": "CannyEdgePreprocessor", + "control_net_name_1": "control_lora_rank128_v11p_sd15_canny_fp16.safetensors", + "strength_1": 1, + "threshold_a_1": 100, + "threshold_b_1": 200, + "start_percent_1": 0, + "end_percent_1": 1, + "resolution_1": 512, + "is_enabled_2": "disable", + "preprocessor_name_2": "OpenposePreprocessor", + "control_net_name_2": "control_lora_rank128_v11p_sd15_openpose_fp16.safetensors", + "strength_2": 1, + "threshold_a_2": 0, + "threshold_b_2": 0, + "start_percent_2": 0, + "end_percent_2": 1, + "resolution_2": 512, + "is_enabled_3": "disable", + "preprocessor_name_3": "InpaintPreprocessor", + "control_net_name_3": "control_lora_rank128_v11p_sd15_inpaint_fp16.safetensors", + "strength_3": 1, + "threshold_a_3": 0, + "threshold_b_3": 0, + "start_percent_3": 0, + "end_percent_3": 1, + "resolution_3": 512, + "positive": [ + "6", + 0 + ], + "negative": [ + "7", + 0 + ] + }, + "class_type": "ControlNetScript" + }, + "91": { + "inputs": { + "images": [ + "81", + 0 + ] + }, + "class_type": "PreviewImage" + }, + "92": { + "inputs": { + "images": [ + "81", + 1 + ] + }, + "class_type": "PreviewImage" + }, + "93": { + "inputs": { + "images": [ + "81", + 2 + ] + }, + "class_type": "PreviewImage" + }, + "98": { + "inputs": { + "stop_at_clip_layer": -1, + "clip": [ + "16", + 1 + ] + }, + "class_type": "CLIPSetLastLayer" + } +} \ No newline at end of file diff --git a/typescripts/comfyui/txt2img_workflow.json b/typescripts/comfyui/txt2img_workflow.json new file mode 100644 index 00000000..e506923e --- /dev/null +++ b/typescripts/comfyui/txt2img_workflow.json @@ -0,0 +1,1668 @@ +{ + "last_node_id": 98, + "last_link_id": 156, + "nodes": [ + { + "id": 8, + "type": "VAEDecode", + "pos": [ + 1238.8565730529783, + 603.497425166778 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 23, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 36 + }, + { + "name": "vae", + "type": "VAE", + "link": 82 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 9 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 55, + "type": "LatentUpscaleBy", + "pos": [ + 1465, + -133 + ], + "size": { + "0": 315, + "1": 82 + }, + "flags": {}, + "order": 24, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 78 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 79 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "Upscale Latent By | id:scaler", + "properties": { + "Node name for S&R": "LatentUpscaleBy" + }, + "widgets_values": [ + "nearest-exact", + 2 + ] + }, + { + "id": 13, + "type": "VAEDecode", + "pos": [ + 1961, + 125 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 27, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 15 + }, + { + "name": "vae", + "type": "VAE", + "link": 83 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 17 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 57, + "type": "VAELoader", + "pos": [ + -1483, + 541 + ], + "size": { + "0": 315, + "1": 58 + }, + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 81 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "Load VAE | id:vae", + "properties": { + "Node name for S&R": "VAELoader" + }, + "widgets_values": [ + "vae-ft-mse-840000-ema-pruned.safetensors" + ] + }, + { + "id": 56, + "type": "Reroute", + "pos": [ + -740, + 606 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 81 + } + ], + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 82, + 83 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 59, + "type": "Reroute", + "pos": [ + -730, + 533 + ], + "size": [ + 82, + 26 + ], + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 89 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 90 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 63, + "type": "Reroute", + "pos": [ + 663, + -211 + ], + "size": [ + 140.8, + 26 + ], + "flags": {}, + "order": 21, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 136 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 105, + 107 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 7, + "type": "CLIPTextEncode", + "pos": [ + 451, + 493 + ], + "size": { + "0": 425.27801513671875, + "1": 180.6060791015625 + }, + "flags": {}, + "order": 14, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 119 + }, + { + "name": "text", + "type": "STRING", + "link": 98, + "widget": { + "name": "text" + } + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 140 + ], + "slot_index": 0 + } + ], + "title": "CLIP Text Encode (Prompt) | id:negative_prompt", + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "" + ] + }, + { + "id": 3, + "type": "KSampler", + "pos": [ + 1190, + -515 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 22, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 93 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 106 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 105 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 39 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 36, + 78 + ], + "slot_index": 0 + } + ], + "title": "KSampler | id:sampler", + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 945794611996037, + "fixed", + 12, + 8, + "dpmpp_sde", + "normal", + 1 + ] + }, + { + "id": 12, + "type": "SaveImage", + "pos": [ + 2240, + 123 + ], + "size": { + "0": 407.53717041015625, + "1": 468.13226318359375 + }, + "flags": {}, + "order": 28, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 17 + } + ], + "title": "Save Image | id:hires_output", + "properties": {}, + "widgets_values": [ + "ComfyUI" + ] + }, + { + "id": 62, + "type": "Reroute", + "pos": [ + 689, + -612 + ], + "size": [ + 140.8, + 26 + ], + "flags": {}, + "order": 20, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 135 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 106, + 108 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": true, + "horizontal": false + } + }, + { + "id": 6, + "type": "CLIPTextEncode", + "pos": [ + 464, + 250 + ], + "size": { + "0": 422.84503173828125, + "1": 164.31304931640625 + }, + "flags": {}, + "order": 15, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 87 + }, + { + "name": "text", + "type": "STRING", + "link": 88, + "widget": { + "name": "text" + } + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 131 + ], + "slot_index": 0 + } + ], + "title": "CLIP Text Encode (Prompt) | id:positive_prompt", + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "masterpiece HDR victorian portrait painting of woman, blonde hair, mountain nature, blue sky\n" + ] + }, + { + "id": 11, + "type": "KSampler", + "pos": [ + 1570, + 130 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 26, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 97, + "slot_index": 0 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 108, + "slot_index": 1 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 107, + "slot_index": 2 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 79, + "slot_index": 3 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 15 + ], + "slot_index": 0 + } + ], + "title": "KSampler | id:hires_sampler", + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 702670332387358, + "fixed", + 14, + 8, + "dpmpp_2m", + "simple", + 0.5 + ] + }, + { + "id": 58, + "type": "LoadLorasFromPrompt", + "pos": [ + -696, + 175 + ], + "size": { + "0": 462, + "1": 200 + }, + "flags": {}, + "order": 13, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 94 + }, + { + "name": "clip", + "type": "CLIP", + "link": 155 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 93, + 97 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 87 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "STRING", + "type": "STRING", + "links": [ + 88 + ], + "shape": 3, + "slot_index": 2 + } + ], + "title": "Load Loras From Prompt | id:multi_loras_positive_prompt", + "properties": { + "Node name for S&R": "LoadLorasFromPrompt" + }, + "widgets_values": [ + " lora_delta girl lora_bright_style girl masterpiece HDR victorian portrait painting of woman, blonde hair, mountain nature, blue sky" + ] + }, + { + "id": 60, + "type": "LoadLorasFromPrompt", + "pos": [ + -78, + 572 + ], + "size": { + "0": 462, + "1": 200 + }, + "flags": {}, + "order": 12, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 90 + }, + { + "name": "clip", + "type": "CLIP", + "link": 156 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 94 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 119 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "STRING", + "type": "STRING", + "links": [ + 98 + ], + "shape": 3, + "slot_index": 2 + } + ], + "title": "Load Loras From Prompt | id:multi_loras_negative_prompt", + "properties": { + "Node name for S&R": "LoadLorasFromPrompt" + }, + "widgets_values": [ + "bad hands, text, watermark" + ] + }, + { + "id": 9, + "type": "SaveImage", + "pos": [ + 1499, + 603 + ], + "size": { + "0": 232.94032287597656, + "1": 282.4336242675781 + }, + "flags": {}, + "order": 25, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9 + } + ], + "properties": {}, + "widgets_values": [ + "ComfyUI" + ] + }, + { + "id": 87, + "type": "LoadImage", + "pos": [ + -150, + -970 + ], + "size": { + "0": 277.20001220703125, + "1": 140.65826416015625 + }, + "flags": {}, + "order": 1, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 148 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_mask_2", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + }, + { + "id": 91, + "type": "PreviewImage", + "pos": [ + 790, + -1290 + ], + "size": { + "0": 336, + "1": 246 + }, + "flags": {}, + "order": 17, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 151 + } + ], + "title": "Preview Image | id:preprocessor_output_1", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 92, + "type": "PreviewImage", + "pos": [ + 1150, + -1290 + ], + "size": { + "0": 336, + "1": 246 + }, + "flags": {}, + "order": 18, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 152 + } + ], + "title": "Preview Image | id:preprocessor_output_2", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 93, + "type": "PreviewImage", + "pos": [ + 1510, + -1290 + ], + "size": { + "0": 336, + "1": 246 + }, + "flags": {}, + "order": 19, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 153 + } + ], + "title": "Preview Image | id:preprocessor_output_3", + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 86, + "type": "LoadImage", + "pos": [ + -480, + -1490 + ], + "size": { + "0": 277.20001220703125, + "1": 244.3121795654297 + }, + "flags": { + "collapsed": false + }, + "order": 2, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 146 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_mask_1", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + }, + { + "id": 5, + "type": "EmptyLatentImage", + "pos": [ + 811, + -775 + ], + "size": { + "0": 315, + "1": 106 + }, + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 39 + ], + "slot_index": 0 + } + ], + "title": "Empty Latent Image | id:latent_image", + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 512, + 512, + 1 + ] + }, + { + "id": 81, + "type": "ControlNetScript", + "pos": [ + 242, + -1099 + ], + "size": { + "0": 506.0240173339844, + "1": 822 + }, + "flags": {}, + "order": 16, + "mode": 0, + "inputs": [ + { + "name": "positive", + "type": "CONDITIONING", + "link": 131 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 140, + "slot_index": 1 + }, + { + "name": "image_1", + "type": "IMAGE", + "link": 137 + }, + { + "name": "mask_1", + "type": "IMAGE", + "link": 146, + "slot_index": 3 + }, + { + "name": "image_2", + "type": "IMAGE", + "link": 147 + }, + { + "name": "mask_2", + "type": "IMAGE", + "link": 148 + }, + { + "name": "image_3", + "type": "IMAGE", + "link": 150 + }, + { + "name": "mask_3", + "type": "IMAGE", + "link": 149 + } + ], + "outputs": [ + { + "name": "preprocessed_image_1", + "type": "IMAGE", + "links": [ + 151 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "preprocessed_image_2", + "type": "IMAGE", + "links": [ + 152 + ], + "shape": 3, + "slot_index": 1 + }, + { + "name": "preprocessed_image_3", + "type": "IMAGE", + "links": [ + 153 + ], + "shape": 3, + "slot_index": 2 + }, + { + "name": "positive", + "type": "CONDITIONING", + "links": [ + 135 + ], + "shape": 3, + "slot_index": 3 + }, + { + "name": "negative", + "type": "CONDITIONING", + "links": [ + 136 + ], + "shape": 3, + "slot_index": 4 + } + ], + "title": "ControlNet Script | id:controlnet_script", + "properties": { + "Node name for S&R": "ControlNetScript" + }, + "widgets_values": [ + "disable", + "CannyEdgePreprocessor", + "control_lora_rank128_v11p_sd15_canny_fp16.safetensors", + 1, + 100, + 200, + 0, + 1, + 512, + "disable", + "OpenposePreprocessor", + "control_lora_rank128_v11p_sd15_openpose_fp16.safetensors", + 1, + 0, + 0, + 0, + 1, + 512, + "disable", + "InpaintPreprocessor", + "control_lora_rank128_v11p_sd15_inpaint_fp16.safetensors", + 1, + 0, + 0, + 0, + 1, + 512 + ] + }, + { + "id": 89, + "type": "LoadImage", + "pos": [ + -300, + -450 + ], + "size": { + "0": 277.20001220703125, + "1": 192.7110595703125 + }, + "flags": {}, + "order": 4, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 149 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_mask_3", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + }, + { + "id": 90, + "type": "LoadImage", + "pos": [ + -670, + -460 + ], + "size": { + "0": 285.6000061035156, + "1": 213.3619384765625 + }, + "flags": {}, + "order": 5, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 150 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_image_3", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + }, + { + "id": 88, + "type": "LoadImage", + "pos": [ + -440, + -970 + ], + "size": { + "0": 285.6000061035156, + "1": 163.2183074951172 + }, + "flags": {}, + "order": 6, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 147 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_image_2", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + }, + { + "id": 64, + "type": "LoadImage", + "pos": [ + -830, + -1490 + ], + "size": { + "0": 306.4342346191406, + "1": 230.96018981933594 + }, + "flags": { + "collapsed": false + }, + "order": 7, + "mode": 2, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 137 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "shape": 3 + } + ], + "title": "Load Image | id:controlnet_image_1", + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "AnimateDiff_00005_ (1) (2).gif", + "image" + ] + }, + { + "id": 16, + "type": "CheckpointLoaderSimple", + "pos": [ + -1473, + 311 + ], + "size": { + "0": 315, + "1": 98 + }, + "flags": {}, + "order": 8, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 89 + ], + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 154 + ], + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [], + "slot_index": 2 + } + ], + "title": "Load Checkpoint | id:checkpoint", + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "dreamshaper_8.safetensors" + ] + }, + { + "id": 98, + "type": "CLIPSetLastLayer", + "pos": [ + -1115, + 292 + ], + "size": { + "0": 315, + "1": 58 + }, + "flags": {}, + "order": 11, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 154 + } + ], + "outputs": [ + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 155, + 156 + ], + "shape": 3, + "slot_index": 0 + } + ], + "title": "CLIP Set Last Layer | id:clip_skip", + "properties": { + "Node name for S&R": "CLIPSetLastLayer" + }, + "widgets_values": [ + -1 + ] + } + ], + "links": [ + [ + 9, + 8, + 0, + 9, + 0, + "IMAGE" + ], + [ + 15, + 11, + 0, + 13, + 0, + "LATENT" + ], + [ + 17, + 13, + 0, + 12, + 0, + "IMAGE" + ], + [ + 36, + 3, + 0, + 8, + 0, + "LATENT" + ], + [ + 39, + 5, + 0, + 3, + 3, + "LATENT" + ], + [ + 78, + 3, + 0, + 55, + 0, + "LATENT" + ], + [ + 79, + 55, + 0, + 11, + 3, + "LATENT" + ], + [ + 81, + 57, + 0, + 56, + 0, + "*" + ], + [ + 82, + 56, + 0, + 8, + 1, + "VAE" + ], + [ + 83, + 56, + 0, + 13, + 1, + "VAE" + ], + [ + 87, + 58, + 1, + 6, + 0, + "CLIP" + ], + [ + 88, + 58, + 2, + 6, + 1, + "STRING" + ], + [ + 89, + 16, + 0, + 59, + 0, + "*" + ], + [ + 90, + 59, + 0, + 60, + 0, + "MODEL" + ], + [ + 93, + 58, + 0, + 3, + 0, + "MODEL" + ], + [ + 94, + 60, + 0, + 58, + 0, + "MODEL" + ], + [ + 97, + 58, + 0, + 11, + 0, + "MODEL" + ], + [ + 98, + 60, + 2, + 7, + 1, + "STRING" + ], + [ + 105, + 63, + 0, + 3, + 2, + "CONDITIONING" + ], + [ + 106, + 62, + 0, + 3, + 1, + "CONDITIONING" + ], + [ + 107, + 63, + 0, + 11, + 2, + "CONDITIONING" + ], + [ + 108, + 62, + 0, + 11, + 1, + "CONDITIONING" + ], + [ + 119, + 60, + 1, + 7, + 0, + "CLIP" + ], + [ + 131, + 6, + 0, + 81, + 0, + "CONDITIONING" + ], + [ + 135, + 81, + 3, + 62, + 0, + "*" + ], + [ + 136, + 81, + 4, + 63, + 0, + "*" + ], + [ + 137, + 64, + 0, + 81, + 2, + "IMAGE" + ], + [ + 140, + 7, + 0, + 81, + 1, + "CONDITIONING" + ], + [ + 146, + 86, + 0, + 81, + 3, + "IMAGE" + ], + [ + 147, + 88, + 0, + 81, + 4, + "IMAGE" + ], + [ + 148, + 87, + 0, + 81, + 5, + "IMAGE" + ], + [ + 149, + 89, + 0, + 81, + 7, + "IMAGE" + ], + [ + 150, + 90, + 0, + 81, + 6, + "IMAGE" + ], + [ + 151, + 81, + 0, + 91, + 0, + "IMAGE" + ], + [ + 152, + 81, + 1, + 92, + 0, + "IMAGE" + ], + [ + 153, + 81, + 2, + 93, + 0, + "IMAGE" + ], + [ + 154, + 16, + 1, + 98, + 0, + "CLIP" + ], + [ + 155, + 98, + 0, + 58, + 1, + "CLIP" + ], + [ + 156, + 98, + 0, + 60, + 1, + "CLIP" + ] + ], + "groups": [ + { + "title": "Txt2Img", + "bounding": [ + 442, + -7, + 1211, + 708 + ], + "color": "#a1309b", + "font_size": 24, + "locked": false + }, + { + "title": "Save Intermediate Image", + "bounding": [ + 1225, + 500, + 516, + 196 + ], + "color": "#3f789e", + "font_size": 24, + "locked": false + }, + { + "title": "Hires Fix", + "bounding": [ + 1210, + 21, + 710, + 464 + ], + "color": "#b58b2a", + "font_size": 24, + "locked": false + }, + { + "title": "Save Final Image", + "bounding": [ + 1949, + 31, + 483, + 199 + ], + "color": "#3f789e", + "font_size": 24, + "locked": false + }, + { + "title": "txt2img workflow", + "bounding": [ + 646, + -1873, + 1027, + 134 + ], + "color": "#3f789e", + "font_size": 24, + "locked": false + } + ], + "config": {}, + "extra": {}, + "version": 0.4 +} \ No newline at end of file diff --git a/typescripts/comfyui/util.ts b/typescripts/comfyui/util.ts new file mode 100644 index 00000000..973d2154 --- /dev/null +++ b/typescripts/comfyui/util.ts @@ -0,0 +1,547 @@ +import { readdirSync, readFileSync } from 'fs' +import { requestPost } from '../util/ts/api' + +import { storage } from 'uxp' +import { reaction, toJS } from 'mobx' +import { AStore } from '../main/astore' + +import comfyapi from './comfyapi' +import { base64UrlToBase64 } from '../util/ts/general' +import { app } from 'photoshop' +export enum InputTypeEnum { + NumberField = 'NumberField', + TextField = 'TextField', + TextArea = 'TextArea', + Menu = 'Menu', + ImageBase64 = 'ImageBase64', +} +export interface ValidInput { + [key: string]: any + value: string | number + label: string + list?: any[] + type: InputTypeEnum + id?: string +} +export interface PhotoshopNode { + inputs: ValidInput[] + id: string +} + +export interface ComfyUIConfig { + [key: string]: any + checkpoints: string[] + samplers: string[] + schedulers: string[] +} + +export interface ComfyUINode { + inputs: any + class_type: string +} +// Assuming the json files are in a directory named 'native_workflows' +const dir = 'plugin:/typescripts/comfyui/native_workflows' // specify the directory containing the .json files +let workflows2: Record = {} +readdirSync(dir).forEach((file) => { + if (file.endsWith('.json')) { + const fileContent = readFileSync(`${dir}/${file}`, 'utf8') + const fileNameWithoutExtension = file.slice(0, -5) + workflows2[fileNameWithoutExtension] = JSON.parse(fileContent) + } +}) + +export const store = new AStore({ + comfyui_valid_nodes: {} as any, // comfyui nodes like structure that contain all info necessary to create plugin ui elements + uuids: {} as any, + + comfyui_output_images: [] as string[], //store the output images from generation + comfyui_output_thumbnail_images: [] as string[], // store thumbnail size images + comfyui_config: {} as ComfyUIConfig, // all config data like samplers, checkpoints ...etc + workflow_path: '', // the path of an image that contains prompt information + workflow_dir_path: '', // the path of the directory that contains all workflow files + // workflows_paths: [] as string[], + // workflows_names: [] as string[], + workflows: {} as any, + selected_workflow_name: '', // the selected workflow from the workflow menu + current_prompt: {} as any, // current prompt extracted from the workflow + thumbnail_image_size: 100, + load_image_nodes: {} as any, //our custom loadImageBase64 nodes, we need to substitute comfyui LoadImage nodes with before generating a prompt + // load_image_base64_strings: {} as any, //images the user added to the plugin comfy ui + object_info: undefined as any, + current_prompt2: {} as any, + current_prompt2_output: {} as any, + output_thumbnail_image_size: {} as Record, + + uploaded_images_base64_url: [] as string[], + current_uploaded_image: {} as Record, //key: node_id, value: base64_url; only used in UI to show the selected images for LoadImage nodes + current_uploaded_video: {} as Record, + uploaded_images_list: [] as string[], // store an array of all images in the comfy's input directory + uploaded_video_list: [] as string[], // store the name of .gif and .mp4 videos in comfy's input directory and subcategories + nodes_order: [] as string[], // nodes with smaller index will be rendered first, + can_edit_nodes: false as boolean, + nodes_label: {} as Record, + + workflows2: workflows2 as Record, + user_custom_workflow: {} as Record, + progress_value: 0, + is_random_seed: {} as Record, + last_moved: undefined as string | undefined, // the last node that has been moved in the edit mode + + base64_to_uploaded_images_names: {} as Record, + can_generate: true as boolean, +}) + +interface Workflow {} +export function getNodes(workflow: Workflow) { + // Object.values(workflow).forEach((node) => { + // console.log(node.class_type) + // }) + return Object.entries(workflow) +} + +export enum ComfyInputType { + TextField = 'TextField', + TextArea = 'TextArea', + Menu = 'Menu', + Number = 'Number', + Slider = 'Slider', + BigNumber = 'BigNumber', + TextFieldNumber = 'TextFieldNumber', + Skip = 'Skip', + Seed = 'Seed', + CheckBox = 'CheckBox', +} +export enum ComfyNodeType { + LoadImage = 'LoadImage', + LoadVideo = 'LoadVideo', + Normal = 'Normal', + Skip = 'Skip', +} + +interface ComfyOutputImage { + filename: string + subfolder: string + type: string +} + +export function getNodeType(node_name: any) { + let node_type: ComfyNodeType = ComfyNodeType.Normal + switch (node_name) { + case 'LoadImage': + node_type = ComfyNodeType.LoadImage + break + case 'LoadVideo': + node_type = ComfyNodeType.LoadVideo + break + + default: + break + } + return node_type +} +export function parseComfyInput( + name: string, + input_info: any, + prompt_value: any // the default value, set in the prompt api +): { + type: ComfyInputType + config: any +} { + let input_type: ComfyInputType = ComfyInputType.Skip + let input_config = input_info?.[1] || void 0 + if (input_info === undefined) { + return { type: input_type, config: input_config } + } + + try { + const value = input_info[0] + + if ( + (name === 'seed' || name === 'noise_seed') && + !Array.isArray(prompt_value) + ) { + input_type = ComfyInputType.Seed // similar to big number + input_config = input_info[1] + } else if (typeof value === 'string') { + if (value === 'FLOAT' && !Array.isArray(prompt_value)) { + if (Number.isSafeInteger(input_config?.max)) { + input_type = ComfyInputType.Slider + input_config = input_info[1] + } else { + input_type = ComfyInputType.TextFieldNumber + input_config = input_info[1] + } + } else if (value === 'INT' && !Array.isArray(prompt_value)) { + if (input_info[1].max > Number.MAX_SAFE_INTEGER) { + input_type = ComfyInputType.BigNumber + input_config = input_info[1] + } else { + input_type = ComfyInputType.TextFieldNumber + input_config = input_info[1] + } + } else if (value === 'STRING' && !Array.isArray(prompt_value)) { + if (input_info[1]?.multiline) { + input_type = ComfyInputType.TextArea + input_config = input_info[1] + } else { + input_type = ComfyInputType.TextField + input_config = input_info[1] + } + } else if (value === 'BOOLEAN' && !Array.isArray(prompt_value)) { + input_type = ComfyInputType.CheckBox + input_config = input_info[1] + } + } else if (Array.isArray(value)) { + input_type = ComfyInputType.Menu + input_config = value + } + } catch (e) { + console.error( + `name:${name}, + input_info:${input_info}, + prompt_value:${prompt_value}`, + e + ) + } + return { type: input_type, config: input_config } +} + +export function makeHtmlInput() {} + +async function getHistory(prompt_id: string) { + while (true) { + const res = await comfyapi.comfy_api.queue() + if (res.queue_pending.length || res.queue_running.length) { + await new Promise((resolve) => setTimeout(resolve, 500)) + } else { + break + } + await new Promise((resolve) => setTimeout(resolve, 500)) + } + const history = await comfyapi.comfy_api.getHistory(prompt_id) + return history +} +export async function postPromptAndGetBase64JsonResult( + prompt: Record, + separated_output_nodes: string[] = [] +): Promise<{ + outputs: Record | undefined + separated_outputs: Record | undefined +}> { + try { + const res = await comfyapi.comfy_api.prompt(prompt) + if (!res) { + throw new Error( + `Unable to establish a connection to ComfyUI at the provided address: ${comfyapi.comfy_api.comfy_url}. Please ensure that ComfyUI is online and the URL is correct.` + ) + } + if (res.error) { + const readable_error = comfyapi.comfy_api.getReadableError(res) + throw new Error(readable_error) + } + const prompt_id = res.prompt_id + const history = await getHistory(prompt_id) + const promptInfo = history[prompt_id] + if (Object.keys(promptInfo.outputs).length === 0) { + throw new Error( + `No images were generated. Please check the ComfyUI console for potential errors.` + ) + } + + const { outputs, separated_outputs } = + await mapComfyOutputToStoreOutput( + promptInfo.outputs, + separated_output_nodes + ) + // // [4][0] for output id. + // const fileName = promptInfo.outputs[promptInfo.prompt[4][0]].images[0].filename + // const resultB64 = await ComfyApi.view(this, fileName); + // resultImages.push(resultB64) + // if (option.imageFinishCallback) { + // try { option.imageFinishCallback(resultB64, index) } catch (e) { } + // } + // } + return { outputs, separated_outputs } + } catch (e) { + console.error(e) + app.showAlert(`${e}`) + return { outputs: undefined, separated_outputs: undefined } + } +} +export const getFileFormat = (fileName: string): string => + fileName.includes('.') ? fileName.split('.').pop()! : '' + +export async function base64UrlFromComfy({ + filename, + type, + subfolder, +}: ComfyOutputImage) { + const base64 = await comfyapi.comfy_api.view(filename, type, subfolder) + return base64Url(base64, getFileFormat(filename)) +} +export function base64UrlFromFileName(base64: string, filename: string) { + return base64Url(base64, getFileFormat(filename)) +} +export function base64Url(base64: string, format: string = 'png') { + return `data:image/${format};base64,${base64}` +} +export function generatePrompt(prompt: Record) { + prompt +} +export function updateOutput(output: any, output_store_obj: any) { + // store.data.current_prompt2_output[26] = [image, image] + output_store_obj = output +} + +export async function mapComfyOutputToStoreOutput( + comfy_output: Record, + separated_output_nodes: string[] = [] +) { + const outputs: Record = {} + const separated_outputs: Record = {} + + for (let key in comfy_output) { + let base64_url_list = await Promise.all( + (Object.values(comfy_output[key]).flat() as ComfyOutputImage[]).map( + async (output: ComfyOutputImage) => { + try { + if (['png'].includes(extractFormat(output.filename))) { + return await base64UrlFromComfy(output) + } else if ( + ['gif'].includes(extractFormat(output.filename)) + ) { + const url = `${comfyapi.comfy_api.comfy_url}/view?subfolder=${output.subfolder}&type=${output.type}&filename=${output.filename}` + return url + } + } catch (e) { + console.error(output, e) + return '' + } + } + ) + ) + base64_url_list = base64_url_list.filter((item) => item !== '') // Filter out empty strings + + if (separated_output_nodes.includes(key)) { + separated_outputs[key] = [ + ...(separated_outputs[key] || []), + ...base64_url_list, + ] + } else { + outputs[key] = [...(outputs[key] || []), ...base64_url_list] + } + } + + return { outputs, separated_outputs } +} + +interface LooseObject { + [key: string]: any +} + +function isSameStructure(obj1: LooseObject, obj2: LooseObject): boolean { + // Get keys + const keys1 = Object.keys(obj1) + const keys2 = Object.keys(obj2) + + // Check if both objects have the same number of keys + if (keys1.length !== keys2.length) { + return false + } + + // Check if all keys in obj1 exist in obj2 and have the same structure + for (let i = 0; i < keys1.length; i++) { + const key = keys1[i] + + // Check if the key exists in obj2 + if (!obj2.hasOwnProperty(key)) { + return false + } + + // If the value of this key is an object, check their structure recursively + if ( + typeof obj1[key] === 'object' && + obj1[key] !== null && + typeof obj2[key] === 'object' && + obj2[key] !== null + ) { + if (!isSameStructure(obj1[key], obj2[key])) { + return false + } + } + } + + // If all checks passed, the structures are the same + return true +} + +function extractFormat(input: string) { + let format: string = '' + if (input.includes('data:')) { + // Case for dataURL + format = input.split(':')[1].split(';')[0].split('/')[1] + } else if (input.includes('.')) { + // Case for file name with extension + format = input.split('.').pop() || '' + } else { + throw `input doesn't have an extension. input:${input}` + } + return format +} + +async function uploadImagePost( + buffer: any, + file_name: string, + subfolder: string = 'auto-photoshop-plugin' +) { + try { + const full_url = comfyapi.comfy_api.comfy_url + '/upload/image' + var formData = new FormData() + + formData.append('image', buffer, file_name) + formData.append('subfolder', subfolder) + var requestOptions = { + method: 'POST', + // header: myHeaders, + body: formData, + } + //@ts-ignore + const res = await fetch(full_url, requestOptions) + + console.log(res.status, full_url) + + const contentType = res.headers.get('Content-Type') + + if (contentType?.indexOf('json') != -1) { + return res.json() + } else { + if (res.status == 200) { + return res.arrayBuffer() + } else { + return res.text() + } + } + } catch (e) { + console.error(e) + } +} +async function uploadImage(b_from_disk = false, imgBase64: string) { + try { + let content, name + if (b_from_disk) { + const res = await readFile() + content = res.content + name = res.name + } else { + //from canvas + //@ts-ignore + const buffer = _base64ToArrayBuffer(imgBase64) + content = buffer + name = 'buffer.png' + } + + // console.log('content: ', content) + // console.log('name: ', name) + + const uploaded_image = await uploadImagePost(content, name, '') + return uploaded_image + } catch (e) { + console.error(e) + } +} + +async function readFile() { + const entry = await storage.localFileSystem.getFileForOpening() // Prompts the user to select a file + // const contents = await entry.read('binary') // Reads the file as a string + const contents = await entry.read({ + format: storage.formats.binary, + }) + return { content: contents, name: entry.name } +} + +function getRandomBigIntApprox(min: bigint, max: bigint): bigint { + min = BigInt(min) + max = BigInt(max) + const range = Number(max - min) + const rand = Math.floor(Math.random() * range) + return BigInt(rand) + min +} +function runRandomSeedScript() { + Object.entries(toJS(store.data.is_random_seed)).forEach( + ([node_id, is_random]) => { + if (is_random) { + const random_seed: bigint = getRandomBigIntApprox( + 0n, + 18446744073709552000n + ) + Object.keys(store.data.current_prompt2[node_id].inputs).forEach( + (name: string) => { + if (['seed', 'noise_seed'].includes(name)) { + store.data.current_prompt2[node_id].inputs[name] = + random_seed.toString() + // Usage + } + } + ) + } + } + ) +} +async function maskExpansion( + base64_mask: string, + expansion: number, + blur: number +) { + const prompt = { + '1': { + inputs: { + mask: base64_mask, + expansion: expansion, + blur: blur, + }, + class_type: 'MaskExpansion', + }, + '6': { + inputs: { + images: ['1', 0], + }, + class_type: 'PreviewImage', + }, + } + try { + const { outputs, separated_outputs } = + await postPromptAndGetBase64JsonResult(prompt) + if (outputs) { + const expanded_mask = outputs['6'][0] + return base64UrlToBase64(expanded_mask) + } + // html_manip.setInitImageMaskSrc(expanded_mask) + } catch (e) { + console.error(e) + } + return base64_mask +} +export default { + uploadImage, + uploadImagePost, + getNodes, + parseComfyInput, + getNodeType, + base64Url, + getFileFormat, + base64UrlFromComfy, + generatePrompt, + updateOutput, + getHistory, + mapComfyOutputToStoreOutput, + postPromptAndGetBase64JsonResult, + isSameStructure, + extractFormat, + getRandomBigIntApprox, + runRandomSeedScript, + maskExpansion, + workflows2, + ComfyInputType, + ComfyNodeType, + store, +} diff --git a/typescripts/controlnet/ControlNetUnit.tsx b/typescripts/controlnet/ControlNetUnit.tsx index 1c82d4be..9e07765d 100644 --- a/typescripts/controlnet/ControlNetUnit.tsx +++ b/typescripts/controlnet/ControlNetUnit.tsx @@ -11,6 +11,7 @@ import { PreviewSvg, SpSliderWithLabel, SliderType, + SearchableMenu, } from '../util/elements' import ControlNetStore, { ControlnetMode, controlnetModes } from './store' import { mapRange, versionCompare } from './util' @@ -135,18 +136,12 @@ export default class ControlNetUnit extends React.Component< storeData.model = filters.default_model } } - onPreprocsesorChange( - event: any, - { index, item }: { index: number; item: string } - ) { + onPreprocsesorChange(item: string) { const storeData = this.props.appState.controlNetUnitData[this.props.index] storeData.module = item } - onModelChange( - event: any, - { index, item }: { index: number; item: string } - ) { + onModelChange(item: string) { const storeData = this.props.appState.controlNetUnitData[this.props.index] storeData.model = item @@ -806,7 +801,7 @@ export default class ControlNetUnit extends React.Component< style={{ display: 'flex' }} >
- */} + { + const default_value = + // storeData.module_list[0] || 'None' + 'None' + + storeData.module = default_value + return default_value + }} + onChange={(item: any) => { + this.onPreprocsesorChange(item) + }} />
{!pd.model_free && (
- */} + { + const default_value = + // storeData.model_list[0] || 'None' + 'None' + storeData.model = default_value + return default_value + }} + onChange={(item: any) => { + this.onModelChange(item) + }} />
)} diff --git a/typescripts/controlnet/entry.ts b/typescripts/controlnet/entry.ts index 43b35c1b..41ae098c 100644 --- a/typescripts/controlnet/entry.ts +++ b/typescripts/controlnet/entry.ts @@ -1,8 +1,9 @@ +import { toJS } from 'mobx' import { setControlImageSrc } from '../../utility/html_manip' // import { session_ts } from '../entry' // import * as session_ts from '../session/session' import { store as session_store } from '../session/session_store' -import { Enum, api, python_replacement } from '../util/oldSystem' +import { Enum, api, io, python_replacement } from '../util/oldSystem' import { GenerationModeEnum } from '../util/ts/enum' import store, { DefaultControlNetUnitData, @@ -14,6 +15,40 @@ const { getExtensionUrl } = python_replacement declare const g_sd_config_obj: any declare let g_sd_url: string +function convertComfyModuleDetailsToPluginModuleDetails( + comfy_module_details: Record +) { + let outputJson: Record = {} + for (let preprocessorName in comfy_module_details) { + let preprocessorConfig = comfy_module_details[preprocessorName] + let sliders = [] + if (preprocessorConfig.resolution) { + sliders.push({ + name: `${preprocessorName} Resolution`, + value: preprocessorConfig.resolution, + min: 64, + max: 2048, + }) + } + if (preprocessorConfig.param_config) { + for (let paramName in preprocessorConfig.param_config) { + let paramConfig = preprocessorConfig.param_config[paramName] + sliders.push({ + name: `${paramName}`, + value: preprocessorConfig[paramName], + min: paramConfig.min, + max: paramConfig.max, + }) + } + } + outputJson[preprocessorName] = { + model_free: false, + sliders: sliders, + } + } + return outputJson +} + async function requestControlNetPreprocessors() { const control_net_json = await api.requestGet( `${g_sd_url}/controlnet/module_list?alias_names=true` @@ -111,6 +146,38 @@ async function initializeControlNetTab(controlnet_max_models: number) { console.warn(e) } } +async function initializeControlNetTabComfyUI( + controlnet_max_models: number, + controlnet_models: string[], + preprocessor_list: string[], + preprocessorDetail: Record +) { + store.maxControlNet = controlnet_max_models || store.maxControlNet + // store.controlnetApiVersion = await requestControlNetApiVersion() + + try { + const models = controlnet_models + store.supportedModels = models || [] + } catch (e) { + console.warn(e) + } + try { + store.supportedPreprocessors = preprocessor_list || [] + + store.preprocessorDetail = + convertComfyModuleDetailsToPluginModuleDetails(preprocessorDetail) + } catch (e) { + console.warn(e) + } + try { + store.controlNetUnitData.forEach((unitData) => { + unitData.module_list = store.supportedPreprocessors + unitData.model_list = store.supportedModels + }) + } catch (e) { + console.warn(e) + } +} function getEnableControlNet(index: number) { if (typeof index == 'undefined') @@ -119,24 +186,33 @@ function getEnableControlNet(index: number) { ) else return store.controlNetUnitData[index || 0].enabled } -function mapPluginSettingsToControlNet(plugin_settings: any) { +async function mapPluginSettingsToControlNet(plugin_settings: any) { const ps = plugin_settings // for shortness let controlnet_units: any[] = [] - function getControlNetInputImage(index: number) { + const controlNetUnits = store.controlNetUnitData + async function getControlNetInputImage(index: number) { try { - const b_sync_input_image = - store.controlNetUnitData[index].auto_image - let input_image = store.controlNetUnitData[index].input_image + const b_sync_input_image = controlNetUnits[index].auto_image + let input_image = controlNetUnits[index].input_image if ( b_sync_input_image && [GenerationModeEnum.Txt2Img].includes(session_store.data.mode) ) { //conditions: 1) txt2img mode 2)auto image on 3)first generation of session - input_image = session_store.data.controlnet_input_image ?? '' - store.controlNetUnitData[index].input_image = input_image - store.controlNetUnitData[index].selection_info = - plugin_settings.selection_info + if ( + session_store.data.generation_number === 1 && + session_store.data.controlnet_input_image === '' + ) { + session_store.data.controlnet_input_image = + await io.getImg2ImgInitImage() + } + if (session_store.data.controlnet_input_image !== '') { + input_image = session_store.data.controlnet_input_image + controlNetUnits[index].input_image = input_image + controlNetUnits[index].selection_info = + plugin_settings.selection_info + } } if ( b_sync_input_image && @@ -149,13 +225,10 @@ function mapPluginSettingsToControlNet(plugin_settings: any) { ) { // img2img mode input_image = session_store.data.init_image - store.controlNetUnitData[index].input_image = input_image - store.controlNetUnitData[index].selection_info = + controlNetUnits[index].input_image = input_image + controlNetUnits[index].selection_info = plugin_settings.selection_info - } else if ( - b_sync_input_image && - store.controlNetUnitData[index].enabled - ) { + } else if (b_sync_input_image && controlNetUnits[index].enabled) { //txt2img mode } @@ -175,9 +248,9 @@ function mapPluginSettingsToControlNet(plugin_settings: any) { //maskless mode } else { //mask related mode - store.controlNetUnitData[index].mask = '' // use the mask from the sd mode + controlNetUnits[index].mask = '' // use the mask from the sd mode } - return store.controlNetUnitData[index].mask + return controlNetUnits[index].mask } catch (e) { console.warn(e) } @@ -185,30 +258,29 @@ function mapPluginSettingsToControlNet(plugin_settings: any) { for (let index = 0; index < store.maxControlNet; index++) { controlnet_units[index] = { enabled: getEnableControlNet(index), - input_image: getControlNetInputImage(index), + input_image: await getControlNetInputImage(index), mask: getControlNetMask(index), - module: store.controlNetUnitData[index].module, - model: store.controlNetUnitData[index].model, - weight: store.controlNetUnitData[index].weight, + module: controlNetUnits[index].module, + model: controlNetUnits[index].model, + weight: controlNetUnits[index].weight, resize_mode: 'Crop and Resize', - lowvram: store.controlNetUnitData[index].lowvram, - processor_res: store.controlNetUnitData[index].processor_res || 512, - threshold_a: store.controlNetUnitData[index].threshold_a, - threshold_b: store.controlNetUnitData[index].threshold_b, + lowvram: controlNetUnits[index].lowvram, + processor_res: controlNetUnits[index].processor_res || 512, + threshold_a: controlNetUnits[index].threshold_a, + threshold_b: controlNetUnits[index].threshold_b, // guidance: , - guidance_start: store.controlNetUnitData[index].guidance_start, - guidance_end: store.controlNetUnitData[index].guidance_end, + guidance_start: controlNetUnits[index].guidance_start, + guidance_end: controlNetUnits[index].guidance_end, } if (store.controlnetApiVersion > 1) { //new controlnet v2 controlnet_units[index].control_mode = - store.controlNetUnitData[index].control_mode + controlNetUnits[index].control_mode controlnet_units[index].pixel_perfect = - store.controlNetUnitData[index].pixel_perfect + controlNetUnits[index].pixel_perfect } else { // old controlnet v1 - controlnet_units[index].guessmode = - store.controlNetUnitData[index].guessmode + controlnet_units[index].guessmode = controlNetUnits[index].guessmode } } @@ -284,6 +356,7 @@ export { requestControlNetMaxUnits, requestControlNetFiltersKeywords, initializeControlNetTab, + initializeControlNetTabComfyUI, getEnableControlNet, mapPluginSettingsToControlNet, getControlNetMaxModelsNumber, diff --git a/typescripts/entry.ts b/typescripts/entry.ts index 16d0c8e0..2950a74c 100644 --- a/typescripts/entry.ts +++ b/typescripts/entry.ts @@ -6,12 +6,13 @@ export * as control_net from './controlnet/entry' export * as after_detailer_script from './after_detailer/after_detailer' export * as ultimate_sd_upscaler from './ultimate_sd_upscaler/ultimate_sd_upscaler' export * as scripts from './ultimate_sd_upscaler/scripts' -export * as main from './main/main' + export * as controlnet_main from './controlnet/main' export * as logger from './util/logger' export * as image_search from './image_search/image_search' export * as history from './history/history' export * as viewer from './viewer/viewer' +export { default as viewer_util } from './viewer/viewer_util' export * as session_ts from './session/session' export { store as session_store } from './session/session_store' export { store as sd_tab_store } from './sd_tab/util' @@ -37,5 +38,10 @@ export * as stores from './stores' export { default as lexica } from './lexical/lexical' export * as api_ts from './util/ts/api' +export * as comfyui from './comfyui/comfyui' export { toJS } from 'mobx' export { default as node_fs } from 'fs' +export { default as comfyui_util } from './comfyui/util' +export { default as comfyui_main_ui } from './comfyui/main_ui' + +export { default as comfyapi } from './comfyui/comfyapi' diff --git a/typescripts/sd_tab/sd_tab.tsx b/typescripts/sd_tab/sd_tab.tsx index bd6b0af4..a87ca693 100644 --- a/typescripts/sd_tab/sd_tab.tsx +++ b/typescripts/sd_tab/sd_tab.tsx @@ -5,7 +5,13 @@ import { observer } from 'mobx-react' import { GenerationModeEnum, ScriptMode } from '../util/ts/enum' import { reaction } from 'mobx' -import { SpCheckBox, SpMenu, SpSlider, SpTextfield } from '../util/elements' +import { + SearchableMenu, + SpCheckBox, + SpMenu, + SpSlider, + SpTextfield, +} from '../util/elements' import { ErrorBoundary } from '../util/errorBoundary' import { Collapsible } from '../util/collapsible' @@ -32,6 +38,8 @@ import { onHeightSliderInput, heightSliderOnChangeEventHandler, loadPresetSettings, + isHiResMode, + comfy_mask_content_config, } from './util' import { general } from '../util/oldSystem' import { requestSwapModel, setInpaintMaskWeight } from '../util/ts/sdapi' @@ -44,6 +52,7 @@ import { getExpandedMask } from '../session/session' import { mapRange } from '../controlnet/util' import { store as preset_store } from '../preset/shared_ui_preset' +import Locale from '../locale/locale' declare let g_version: string @@ -96,6 +105,22 @@ const Modes = observer(() => { > Lasso Mode + { + helper_store.data.make_square = + !helper_store.data.make_square + }} + checked={helper_store.data.make_square} + // id={`chEnableControlNet_${this.props.index}`} + > + Make Square + { async componentDidMount() { try { await refreshUI() - await refreshModels() await initPlugin() - helper_store.data.loras = await requestLoraModels() + initInitMaskElement() - helper_store.data.hr_upscaler_list = - await requestGetHiResUpscalers() const btnSquareClass = document.getElementsByClassName('btnSquare') //REFACTOR: move to events.js for (let btnSquareButton of btnSquareClass) { @@ -199,66 +221,85 @@ class SDTab extends React.Component<{}> { } return (
-
- { - return lora.name - })} + // items={helper_store.data.loras.map((lora: any) => { + // return lora.name + // })} + items={helper_store.data.loras} label_item="Select Lora" // selected_index={store.data.models // .map((model) => { @@ -272,8 +313,19 @@ class SDTab extends React.Component<{}> { positive: `${prompt} ${lora_prompt}`, }) }} - > - */} + { + const lora_prompt = getLoraModelPrompt(item) + const prompt = multiPrompts.getPrompt().positive + multiPrompts.setPrompt({ + positive: `${prompt} ${lora_prompt}`, + }) + }} + /> + {/* { positive: `${prompt} ${value.item}`, }) }} - > + > */} + { + const prompt = multiPrompts.getPrompt().positive + multiPrompts.setPrompt({ + positive: `${prompt} ${item}`, + }) + }} + /> { padding: '3px', }} > - +
@@ -363,7 +428,7 @@ class SDTab extends React.Component<{}> { : void 0 } > - {config.name} + {Locale(config.name)} ) })} @@ -465,7 +530,7 @@ class SDTab extends React.Component<{}> { alignItems: 'flex-start', }} > - Batch Size: + {Locale('Batch Size:')} { alignItems: 'flex-start', }} > - Batch Count: + {Locale('Batch Count:')} { }} > - Sampling Steps + {Locale('Sampling Steps:')} {
- Selection Mode: + {Locale('Selection Mode:')}
@@ -575,7 +640,7 @@ class SDTab extends React.Component<{}> { } }} > - {selection_mode.name} + {Locale(selection_mode.name)} ) } @@ -705,7 +770,7 @@ class SDTab extends React.Component<{}> { }} > - Width: + {Locale('Width:')} { }} > - Height: + {Locale('Height:')} { }} > - CFG Scale: + {Locale('CFG Scale:')} @@ -814,7 +879,7 @@ class SDTab extends React.Component<{}> { }} > - Denoising Strength: + {Locale('Denoising Strength:')} {store.data.denoising_strength.toFixed(2)} @@ -933,7 +998,9 @@ class SDTab extends React.Component<{}> { } }} > - Mask Expansion: + + {Locale('Mask Expansion:')} +
@@ -997,30 +1064,31 @@ class SDTab extends React.Component<{}> { Mask Content: - {mask_content_config.map( - (mask_content, index: number) => { - return ( - { + return ( + { + store.data.inpainting_fill = mask_content.value - ? true - : void 0 - } - value={mask_content.value} - onClick={(evt: any) => { - store.data.inpainting_fill = - mask_content.value - }} - > - {mask_content.name} - - ) - } - )} + }} + > + {Locale(`${mask_content.name}`)} + + ) + })}
@@ -1053,24 +1121,20 @@ class SDTab extends React.Component<{}> { evt.target.checked }} > - Restore Faces + {Locale('Restore Faces')} { store.data.enable_hr = evt.target.checked }} > - Hi Res Fix + {Locale('Hi Res Fix')} { store.data.tiling = evt.target.checked }} > - tiling + {Locale('Tiling')}
{
- Seed: + + {Locale('Seed:')} + { store.data.seed = '-1' }} > - Random + {Locale('Random')}
{session_store.data.can_generate ? (
@@ -301,12 +356,14 @@ export class Settings extends React.Component<{}> { store.data.use_sharp_mask = evt.target.checked }} > - use sharp mask + {Locale('use sharp mask')}
- Select Extension: + + {Locale('Select Extension:')} + {[ ExtensionTypeEnum.ProxyServer, ExtensionTypeEnum.Auto1111Extension, @@ -329,7 +386,7 @@ export class Settings extends React.Component<{}> { evt.target.value }} > - {config[extension_type].label} + {Locale(config[extension_type].label)} ) })} @@ -352,7 +409,7 @@ export class Settings extends React.Component<{}> { ) }} > - Turn Off Server Status Alert + {Locale('Turn Off Server Status Alert')}
@@ -394,7 +451,7 @@ export class Settings extends React.Component<{}> { : false }} > - Smart Object + {Locale('Smart Object')}
diff --git a/typescripts/main/main.tsx b/typescripts/settings/vae.tsx similarity index 84% rename from typescripts/main/main.tsx rename to typescripts/settings/vae.tsx index cd8cc285..3d4559c1 100644 --- a/typescripts/main/main.tsx +++ b/typescripts/settings/vae.tsx @@ -1,7 +1,7 @@ import React from 'react' import ReactDOM from 'react-dom/client' import { observer } from 'mobx-react' -import { AStore } from './astore' +import { AStore } from '../main/astore' import { SpMenu } from '../util/elements' import { api, python_replacement } from '../util/oldSystem' @@ -10,27 +10,11 @@ import '../locale/locale-for-old-html' import { ErrorBoundary } from '../util/errorBoundary' declare let g_sd_url: string -// class SDStore extends AStore { -// constructor(data: any) { -// super(data) -// } -// } -// const configValues = Object.entries(ui_config).reduce( -// (acc, [key, value]) => ({ ...acc, [key]: value.value }), -// {} -// ) -// const default_values: any = { -// _: '', -// ...configValues, -// } - -const default_values: any = { - vae_model_list: [], - current_vae: '', -} - -export const store = new AStore(default_values) +export const store = new AStore({ + vae_model_list: [] as string[], + current_vae: '' as string, +}) @observer export class VAEComponent extends React.Component<{ @@ -54,7 +38,6 @@ export class VAEComponent extends React.Component<{ // ) + +export default { + store, + populateVAE, +} diff --git a/typescripts/tool_bar/tool_bar.tsx b/typescripts/tool_bar/tool_bar.tsx index af8481bb..fe8609bf 100644 --- a/typescripts/tool_bar/tool_bar.tsx +++ b/typescripts/tool_bar/tool_bar.tsx @@ -137,8 +137,7 @@ class ToolBar extends React.Component<{}> { }} > @@ -501,3 +504,193 @@ export const ScriptInstallComponent = observer( ) } ) + +@observer +export class SearchableMenu extends React.Component<{ + allItems: string[] + placeholder: string + onChange?: any + selected_item?: string + onSelectItemFailure?: any + // default_value?: string + // searchQuery: string +}> { + state = { + selectedItem: this.props.allItems ? this.props.allItems[0] : undefined, + searchItems: this.props.allItems, + // searchQuery: this.props.searchQuery, + searchQuery: '', + openMenu: false, + } + selectItem(selected_item: string) { + let final_value = this.props.allItems.includes(selected_item) + ? selected_item + : this.props.onSelectItemFailure && this.props.onSelectItemFailure() + + this.setState({ searchQuery: final_value }) + } + componentDidMount(): void { + if (this.props.selected_item) { + // console.log('selected_item has changed:', this.props.selected_item) + this.selectItem(this.props.selected_item) + } + } + componentDidUpdate(prevProps: any, prevState: { searchQuery: string }) { + if (prevState.searchQuery !== this.state.searchQuery) { + this.searchItem(this.state.searchQuery) + } + if (prevProps.selected_item !== this.props.selected_item) { + console.log('selected_item has changed:', this.props.selected_item) + + if (this.props.selected_item !== undefined) { + this.selectItem(this.props.selected_item) + } + } + } + + // searchItem = (event: React.KeyboardEvent) => { + searchItem = (searchQuery: string) => { + console.log('onInput') + + // const filter = event.currentTarget.value.toUpperCase() + const filter = searchQuery.toUpperCase() + + const searchItems = this.props.allItems.filter((item) => + item.toUpperCase().includes(filter) + ) + this.setState({ searchItems: searchItems }) + + console.log( + 'this.props.searchItems.length: ', + this.state.searchItems.length + ) + + this.setState({ + openMenu: this.state.searchItems.length > 0 ? true : false, + }) + + console.log('this.state.openMenu: ', this.state.openMenu) + } + render() { + return ( +
+
+ { + this.setState({ searchQuery: evt.target.value }) + // this.searchItem(evt.taret) + console.log('onInput:', evt.target.value) + + console.log( + 'this.state.searchQuery: ', + this.state.searchQuery + ) + }} + onChange={(evt: any) => { + this.setState({ searchQuery: evt.target.value }) + console.log('onChange:', evt.target.value) + + console.log( + 'this.state.searchQuery: ', + this.state.searchQuery + ) + }} + onFocus={(evt: any) => { + console.log('onFocus:', evt.target.value) + if (evt.target.value === '') { + this.setState({ + searchItems: this.props.allItems, + }) + } + this.setState({ openMenu: true }) + console.log( + 'this.state.searchQuery: ', + this.state.searchQuery + ) + }} + onBlur={(evt: any) => { + setTimeout( + () => { + console.log('onBlur:', evt.target.value) + + const state_values: Record = {} + if ( + !this.props.allItems.includes( + this.state.searchQuery + ) + ) { + state_values.searchQuery = '' + + if (this.props.onSelectItemFailure) { + state_values.searchQuery = + this.props.onSelectItemFailure() + } + } + state_values.openMenu = false + this.setState(state_values) + + console.log( + 'this.state.searchQuery: ', + this.state.searchQuery + ) + }, + + 200 + ) + }} + > + +
    + {this.state.searchItems.map((item: string, index) => ( +
  • { + console.log('onClick:') + + this.setState({ + searchQuery: item, + openMenu: false, + }) + console.log( + 'this.state.searchQuery: ', + this.state.searchQuery + ) + console.log('item: ', item) + if (this.props.onChange) { + this.props.onChange(item) + } + }} + style={{ zIndex: 1000 }} + > + + {item} + +
  • + ))} +
+
+
+ ) + } +} diff --git a/typescripts/util/errorBoundary.tsx b/typescripts/util/errorBoundary.tsx index a9439e30..afaa8874 100644 --- a/typescripts/util/errorBoundary.tsx +++ b/typescripts/util/errorBoundary.tsx @@ -1,4 +1,5 @@ import React, { Component, ErrorInfo, ReactNode } from 'react' +import { refreshUI } from '../sd_tab/util' interface Props { children: ReactNode @@ -6,14 +7,21 @@ interface Props { interface State { hasError: boolean + key: number +} +interface State { + hasError: boolean + key: number } export class ErrorBoundary extends Component { public state: State = { hasError: false, + key: 0, } - public static getDerivedStateFromError(_: Error): State { + public static getDerivedStateFromError(_: Error): Partial { + // When an error occurs, we update state so the next render will show the fallback UI. return { hasError: true } } @@ -21,11 +29,25 @@ export class ErrorBoundary extends Component { console.error('Uncaught error:', error, errorInfo) } + handleRefresh = () => { + this.setState((prevState) => ({ + hasError: false, // reset the error state + key: prevState.key + 1, // increment key to remount children + })) + // await refreshUI() + } + public render() { if (this.state.hasError) { - return

Sorry.. there was an error

+ return ( +
+

Sorry.. there was an error

+ +
+ ) } - return this.props.children + // The key prop causes a remount of children when it changes + return
{this.props.children}
} } diff --git a/typescripts/util/grid.tsx b/typescripts/util/grid.tsx index dcc01fcf..51902a9b 100644 --- a/typescripts/util/grid.tsx +++ b/typescripts/util/grid.tsx @@ -58,7 +58,6 @@ export class Grid extends React.Component<{ event ) } - this.props?.callback(index, event) //todo: is this a typo why do we call callback twice? } } catch (e) { console.warn( diff --git a/typescripts/util/ts/api.ts b/typescripts/util/ts/api.ts index 005a489a..5cc2dba0 100644 --- a/typescripts/util/ts/api.ts +++ b/typescripts/util/ts/api.ts @@ -2,7 +2,7 @@ import { storage } from 'uxp' declare let g_sd_url: string export async function requestGet(url: string) { - let json = null + let data = null const full_url = url try { @@ -11,14 +11,31 @@ export async function requestGet(url: string) { return null } - json = await request.json() + const contentType = request.headers.get('content-type') + if (contentType && contentType.includes('application/json')) { + data = await request.json() + } else if ( + contentType && + contentType.includes('application/octet-stream') + ) { + data = await request.arrayBuffer() + } else if ( + contentType && + (contentType.includes('image/jpeg') || + contentType.includes('image/png')) + ) { + data = await request.arrayBuffer() + } else { + data = await request.text() + } - // console.log('json: ', json) + // console.log('data: ', data) } catch (e) { console.warn(`issues requesting from ${full_url}`, e) } - return json + return data } + export async function requestPost(url: string, payload: any) { let json = null diff --git a/typescripts/util/ts/general.ts b/typescripts/util/ts/general.ts index 0d46fdb8..4486b4f6 100644 --- a/typescripts/util/ts/general.ts +++ b/typescripts/util/ts/general.ts @@ -49,3 +49,21 @@ export function autoResize(textarea: any, text_content: string, delay = 300) { export async function urlToCanvas(url: string, image_name = 'image.png') { await io.IO.urlToLayer(url, image_name) } + +export const copyJson = (originalObject: any) => + JSON.parse(JSON.stringify(originalObject)) + +export function base64ToBase64Url(base64_image: string) { + return 'data:image/png;base64,' + base64_image +} +export function base64UrlToBase64(base64_url: string) { + const base64_image = base64_url.replace('data:image/png;base64,', '') + return base64_image +} + +export function newOutputImageName(format = 'png') { + const random_id = Math.floor(Math.random() * 100000000000 + 1) // Date.now() doesn't have enough resolution to avoid duplicate + const image_name = `output- ${Date.now()}-${random_id}.${format}` + console.log('generated image name:', image_name) + return image_name +} diff --git a/typescripts/util/ts/sdapi.ts b/typescripts/util/ts/sdapi.ts index c4dd2954..d062a9da 100644 --- a/typescripts/util/ts/sdapi.ts +++ b/typescripts/util/ts/sdapi.ts @@ -61,8 +61,10 @@ export async function requestGetModels() { let json = [] const full_url = `${g_sd_url}/sdapi/v1/sd-models` try { - let request = await fetch(full_url) - json = await request.json() + let res = await fetch(full_url) + if (res.status == 200) { + json = await res.json() + } console.log('models json:') console.dir(json) } catch (e) { diff --git a/typescripts/viewer/viewer_util.ts b/typescripts/viewer/viewer_util.ts index 9f919d96..3dfb7781 100644 --- a/typescripts/viewer/viewer_util.ts +++ b/typescripts/viewer/viewer_util.ts @@ -136,3 +136,9 @@ export const resetViewer = () => { init_store.data.images = [] init_store.data.thumbnails = [] } + +export default { + store, + init_store, + mask_store, +} diff --git a/utility/io.js b/utility/io.js index 2451237a..0c30ddd7 100644 --- a/utility/io.js +++ b/utility/io.js @@ -890,10 +890,17 @@ async function createThumbnail(base64Image, width = 100) { return thumbnail } -async function getImageFromCanvas() { - const width = html_manip.getWidth() - const height = html_manip.getHeight() +async function getImageFromCanvas(scale_to_sliders = true) { + let width + let height const selectionInfo = await psapi.getSelectionInfoExe() + if (scale_to_sliders) { + width = html_manip.getWidth() + height = html_manip.getHeight() + } else { + width = selectionInfo.width + height = selectionInfo.height + } const base64 = await io.IO.getSelectionFromCanvasAsBase64Interface_New( width, height, diff --git a/utility/session.js b/utility/session.js index ba93f9d6..13e0cf71 100644 --- a/utility/session.js +++ b/utility/session.js @@ -183,7 +183,7 @@ async function getSettings(session_data) { try { const extension_type = settings_tab_ts.store.data.extension_type // get the extension type - payload['selection_info'] = session_data.selectionInfo + payload['selection_info'] = session_data?.selectionInfo const numberOfBatchSize = parseInt(sd_tab_store.data.batch_size) const prompt = multiPrompts.getPrompt().positive @@ -195,11 +195,14 @@ async function getSettings(session_data) { function calculateSeed(init_seed, batch_index, batch_size) { if (init_seed === -1) return -1 - const seed = init_seed + batch_index * batch_size + // const seed = init_seed + batch_index * batch_size + const seed = init_seed + BigInt(batch_index) * BigInt(batch_size) return seed } - const init_seed = parseInt(sd_tab_store.data.seed) + // const init_seed = parseInt(sd_tab_store.data.seed) + const init_seed = BigInt(sd_tab_store.data.seed) + const seed = calculateSeed( init_seed, g_current_batch_index, @@ -247,7 +250,8 @@ async function getSettings(session_data) { const sampler_name = sd_tab_store.data.sampler_name - const mode = sd_tab_store.data.rb_mode + const mode = session_data?.rb_mode || sd_tab_store.data.rb_mode // Use the 'rb_mode' from the session, if not available, fallback to the 'rb_mode' from the interface. + const b_restore_faces = sd_tab_store.data.restore_faces let denoising_strength = h_denoising_strength @@ -412,6 +416,8 @@ async function getSettings(session_data) { payload['original_negative_prompt'] = negative_prompt } + payload['clip_skip'] = + settings_tab_ts.store.data.CLIP_stop_at_last_layers payload = { ...payload, // prompt: prompt, @@ -424,8 +430,8 @@ async function getSettings(session_data) { denoising_strength: denoising_strength, batch_size: numberOfBatchSize, cfg_scale: cfg_scale, - seed: seed, - // mask_blur: mask_blur, // don't use auto1111 blur, instead use Auto-Photoshop-SD blur + seed: seed.toString(), + mask_blur: 4, //mask_blur, // don't use auto1111 blur, instead use Auto-Photoshop-SD blur use_sharp_mask: use_sharp_mask, use_prompt_shortcut: bUsePromptShortcut, prompt_shortcut_ui_dict: prompt_shortcut_ui_dict,