| import { app } from '../../../scripts/app.js' |
| import { api } from '../../../scripts/api.js' |
| |
| import { $el } from '../../../scripts/ui.js' |
| import { applyTextReplacements } from '../../../scripts/utils.js' |
|
|
| function loadImageToCanvas (base64Image) { |
| var img = new Image() |
| var canvas = document.createElement('canvas') |
| var ctx = canvas.getContext('2d') |
| return new Promise((res, rej) => { |
| img.onload = function () { |
| |
| var width = img.width |
| var height = img.height |
| var max_width = 1024 |
| if (width > max_width) { |
| height *= max_width / width |
| width = max_width |
| } |
|
|
| |
| canvas.width = width |
| canvas.height = height |
|
|
| |
| ctx.drawImage(img, 0, 0, width, height) |
|
|
| |
| var canvasData = canvas.toDataURL() |
| res(canvasData) |
| } |
|
|
| img.src = base64Image |
| }) |
| } |
|
|
| async function uploadImage (blob, fileType = '.svg', filename) { |
| |
| const body = new FormData() |
| body.append( |
| 'image', |
| new File([blob], (filename || new Date().getTime()) + fileType) |
| ) |
|
|
| const resp = await api.fetchApi('/upload/image', { |
| method: 'POST', |
| body |
| }) |
|
|
| |
| let data = await resp.json() |
| let { name, subfolder } = data |
| let src = api.apiURL( |
| `/view?filename=${encodeURIComponent( |
| name |
| )}&type=input&subfolder=${subfolder}${app.getPreviewFormatParam()}${app.getRandParam()}` |
| ) |
|
|
| return src |
| } |
|
|
| const base64Df = |
| 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAALZJREFUKFOFkLERwjAQBPdbgBkInECGaMLUQDsE0AkRVRAYWqAByxldPPOWHwnw4OBGye1p50UDSoA+W2ABLPN7i+C5dyC6R/uiAUXRQCs0bXoNIu4QPQzAxDKxHoALOrZcqtiyR/T6CXw7+3IGHhkYcy6BOR2izwT8LptG8rbMiCRAUb+CQ6WzQVb0SNOi5Z2/nX35DRyb/ENazhpWKoGwrpD6nICp5c2qogc4of+c7QcrhgF4Aa/aoAFHiL+RAAAAAElFTkSuQmCC' |
|
|
| function base64ToBlobFromURL (base64URL, contentType) { |
| return fetch(base64URL).then(response => response.blob()) |
| } |
|
|
| function getContentTypeFromBase64 (base64Data) { |
| const regex = /^data:(.+);base64,/ |
| const matches = base64Data.match(regex) |
| if (matches && matches.length >= 2) { |
| return matches[1] |
| } |
| return null |
| } |
|
|
| |
| |
| |
| |
|
|
| |
| |
| |
|
|
| |
| |
|
|
| function get_position_style (ctx, widget_width, y, node_height) { |
| const MARGIN = 4 |
|
|
| |
| const elRect = ctx.canvas.getBoundingClientRect() |
| const transform = new DOMMatrix() |
| .scaleSelf( |
| elRect.width / ctx.canvas.width, |
| elRect.height / ctx.canvas.height |
| ) |
| .multiplySelf(ctx.getTransform()) |
| .translateSelf(MARGIN, MARGIN + y) |
|
|
| return { |
| transformOrigin: '0 0', |
| transform: transform, |
| left: `0`, |
| top: `0`, |
| cursor: 'pointer', |
| position: 'absolute', |
| maxWidth: `${widget_width - MARGIN * 2}px`, |
| |
| width: `${widget_width - MARGIN * 2}px`, |
| |
| |
| display: 'flex', |
| flexDirection: 'column', |
| |
| justifyContent: 'space-around' |
| } |
| } |
|
|
| const getLocalData = key => { |
| let data = {} |
| try { |
| data = JSON.parse(localStorage.getItem(key)) || {} |
| } catch (error) { |
| return {} |
| } |
| return data |
| } |
|
|
| const setLocalDataOfWin = (key, value) => { |
| localStorage.setItem(key, JSON.stringify(value)) |
| |
| } |
|
|
| function createImage (url) { |
| let im = new Image() |
| return new Promise((res, rej) => { |
| im.onload = () => res(im) |
| im.src = url |
| }) |
| } |
|
|
| const parseImageToBase64 = url => { |
| return new Promise((res, rej) => { |
| fetch(url) |
| .then(response => response.blob()) |
| .then(blob => { |
| const reader = new FileReader() |
| reader.onloadend = () => { |
| const base64data = reader.result |
| res(base64data) |
| |
| } |
| reader.readAsDataURL(blob) |
| }) |
| .catch(error => { |
| console.log('发生错误:', error) |
| }) |
| }) |
| } |
|
|
| const parseSvg = async svgContent => { |
| let scale = 2 |
| |
| const tempContainer = document.createElement('div') |
| tempContainer.innerHTML = svgContent |
|
|
| |
| const svgElement = tempContainer.querySelector('svg') |
| if (!svgElement) return |
| |
| var rectElements = svgElement?.querySelectorAll('rect') || [] |
| |
| |
| var data = [] |
|
|
| Array.from(rectElements, (rectElement, i) => { |
| |
| var x = ~~(rectElement.getAttribute('x') || 0) |
| var y = ~~(rectElement.getAttribute('y') || 0) |
| var width = ~~rectElement.getAttribute('width') |
| var height = ~~rectElement.getAttribute('height') |
| |
| if (x != undefined && y != undefined && width && height) { |
| |
| var canvas = document.createElement('canvas') |
| canvas.width = width |
| canvas.height = height |
| var context = canvas.getContext('2d') |
|
|
| |
| var fill = rectElement.getAttribute('fill') |
| context.fillStyle = fill |
| context.fillRect(0, 0, width, height) |
|
|
| |
| var base64 = canvas.toDataURL() |
|
|
| |
|
|
| var rectData = { |
| x: parseInt(x), |
| y: parseInt(y), |
| width: parseInt(width), |
| height: parseInt(height), |
| z_index: i + 1, |
| scale_option: 'width', |
| image: base64, |
| mask: base64, |
| type: 'base64', |
| _t: 'rect' |
| } |
|
|
| |
| data.push(rectData) |
| } |
| }) |
|
|
| var svgWidth = svgElement.getAttribute('width') |
| var svgHeight = svgElement.getAttribute('height') |
|
|
| if (!(svgWidth && svgHeight)) { |
| |
| let viewBox = svgElement.viewBox.baseVal |
|
|
| svgWidth = viewBox.width |
| svgHeight = viewBox.height |
| } else { |
| try { |
| svgWidth = ~~svgWidth.replace('px', '') |
| svgHeight = ~~svgHeight.replace('px', '') |
| } catch (error) {} |
| } |
|
|
| |
| var canvas = document.createElement('canvas') |
| canvas.width = svgWidth |
| canvas.height = svgHeight |
| var context = canvas.getContext('2d') |
| |
| var svgString = new XMLSerializer().serializeToString(svgElement) |
| var DOMURL = window.URL || window.webkitURL || window |
|
|
| var svgBlob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' }) |
| var url = DOMURL.createObjectURL(svgBlob) |
|
|
| let img = await createImage(url) |
| context.drawImage(img, 0, 0) |
|
|
| let base64 = canvas.toDataURL() |
|
|
| var rectData = { |
| x: 0, |
| y: 0, |
| width: parseInt(svgWidth), |
| height: parseInt(svgHeight), |
| z_index: 0, |
| scale_option: 'width', |
| image: base64, |
| mask: base64, |
| type: 'base64', |
| _t: 'canvas' |
| } |
| data.push(rectData) |
|
|
| |
| console.log('layers', { data, image: base64, svgElement }) |
| return { data, image: base64, svgElement } |
| } |
|
|
| function exportModelViewerImage ( |
| modelViewer, |
| width, |
| height, |
| format = 'image/png', |
| quality = 1.0 |
| ) { |
| const canvas = document.createElement('canvas') |
| canvas.width = width |
| canvas.height = height |
| const context = canvas.getContext('2d') |
|
|
| return new Promise((resolve, reject) => { |
| context.drawImage(modelViewer, 0, 0, width, height) |
|
|
| resolve(canvas.toDataURL(format, quality)) |
| }) |
| } |
|
|
| app.registerExtension({ |
| name: 'Mixlab.image.SvgImage', |
| async getCustomWidgets (app) { |
| return { |
| SVG (node, inputName, inputData, app) { |
| |
| const widget = { |
| type: inputData[0], |
| name: inputName, |
| size: [128, 88], |
| draw (ctx, node, width, y) {}, |
| computeSize (...args) { |
| return [128, 88] |
| }, |
| async serializeValue (nodeId, widgetIndex) { |
| let d = getLocalData('_mixlab_svg_image') |
| |
| if (d) { |
| let url = d[node.id] |
| let dt = await fetch(url) |
| let svgStr = await dt.text() |
| const { data, image } = (await parseSvg(svgStr)) || {} |
| |
| return JSON.parse(JSON.stringify({ data, image })) |
| } else { |
| return |
| } |
| } |
| } |
|
|
| |
| |
| node.addCustomWidget(widget) |
|
|
| return widget |
| } |
| } |
| }, |
|
|
| async beforeRegisterNodeDef (nodeType, nodeData, app) { |
| if (nodeType.comfyClass == 'SvgImage') { |
| const orig_nodeCreated = nodeType.prototype.onNodeCreated |
| nodeType.prototype.onNodeCreated = async function () { |
| orig_nodeCreated?.apply(this, arguments) |
|
|
| const uploadWidget = this.widgets.filter(w => w.name == 'upload')[0] |
| |
|
|
| const widget = { |
| type: 'div', |
| name: 'upload-preview', |
| draw (ctx, node, widget_width, y, widget_height) { |
| Object.assign( |
| this.div.style, |
| get_position_style(ctx, widget_width, 44, node.size[1]) |
| ) |
| } |
| } |
|
|
| widget.div = $el('div', {}) |
|
|
| document.body.appendChild(widget.div) |
|
|
| const inputDiv = (key, placeholder, svgContainer) => { |
| let div = document.createElement('div') |
| const ip = document.createElement('input') |
| ip.type = 'file' |
| ip.className = `${'comfy-multiline-input'} ${placeholder}` |
| div.style = `display: flex; |
| align-items: center; |
| margin: 6px 8px; |
| margin-top: 0;` |
| ip.placeholder = placeholder |
| |
|
|
| ip.style = `outline: none; |
| border: none; |
| padding: 4px; |
| width: 60%;cursor: pointer; |
| height: 32px;` |
| const label = document.createElement('label') |
| label.style = 'font-size: 10px;min-width:32px' |
| label.innerText = placeholder |
| div.appendChild(label) |
| div.appendChild(ip) |
|
|
| let that = this |
|
|
| ip.addEventListener('change', event => { |
| const file = event.target.files[0] |
| const reader = new FileReader() |
|
|
| |
| reader.onload = async e => { |
| const svgContent = e.target.result |
|
|
| var blob = new Blob([svgContent], { type: 'image/svg+xml' }) |
| let url = await uploadImage(blob) |
| |
| const { svgElement, data, image } = await parseSvg(svgContent) |
| |
| let dd = getLocalData(key) |
| dd[that.id] = url |
| setLocalDataOfWin(key, dd) |
| |
|
|
| svgElement.style = `width: 90%;padding: 5%;height: auto;` |
| |
|
|
| svgContainer.innerHTML = '' |
| svgContainer.appendChild(svgElement) |
| let h = ~~getComputedStyle(svgElement).height.replace('px', '') |
| if (that.size && that.size[1] < h) { |
| that.setSize([that.size[0], that.size[1] + h]) |
| app.canvas.draw(true, true) |
| } |
| |
|
|
| uploadWidget.value = await uploadWidget.serializeValue() |
| } |
|
|
| |
| reader.readAsText(file) |
| }) |
| return div |
| } |
|
|
| let svg = document.createElement('div') |
| svg.className = 'preview' |
| svg.style = `background:#eee;margin-top: 12px;` |
|
|
| let upload = inputDiv('_mixlab_svg_image', 'Svg', svg) |
|
|
| widget.div.appendChild(upload) |
| widget.div.appendChild(svg) |
| this.addCustomWidget(widget) |
|
|
| const onRemoved = this.onRemoved |
| this.onRemoved = () => { |
| upload.remove() |
| svg.remove() |
| widget.div.remove() |
| return onRemoved?.() |
| } |
|
|
| if (this.onResize) { |
| this.onResize(this.size) |
| } |
|
|
| this.serialize_widgets = true |
| } |
| } |
| }, |
| async loadedGraphNode (node, app) { |
| |
| |
| const sleep = (t = 1000) => { |
| return new Promise((res, rej) => { |
| setTimeout(() => res(1), t) |
| }) |
| } |
| if (node.type === 'SvgImage') { |
| |
| let widget = node.widgets.filter(w => w.name === 'upload-preview')[0] |
|
|
| let dd = getLocalData('_mixlab_svg_image') |
|
|
| let id = node.id |
| console.log('SvgImage load', node.widgets[0], node.widgets) |
| if (!dd[id]) return |
| let dt = await fetch(dd[id]) |
| let svgStr = await dt.text() |
|
|
| const { svgElement, data, image } = await parseSvg(svgStr) |
| svgElement.style = `width: 90%;padding: 5%;height:auto` |
| |
|
|
| widget.div.querySelector('.preview').innerHTML = '' |
| widget.div.querySelector('.preview').appendChild(svgElement) |
|
|
| const uploadWidget = node.widgets.filter(w => w.name == 'upload')[0] |
| uploadWidget.value = await uploadWidget.serializeValue() |
| } |
| } |
| }) |
|
|
| const createSelect = (imgDiv, select, opts, targetWidget, textWidget) => { |
| select.style.display = 'block' |
| let html = '' |
| let isMatch = false |
| for (const opt of opts) { |
| html += `<option value='${opt.keyword}' ${opt.selected ? 'selected' : ''}>${ |
| opt.keyword |
| }</option>` |
| if (opt.selected) { |
| isMatch = true |
| imgDiv.src = opt.imgurl |
| |
| } |
| } |
| select.innerHTML = html |
| if (!isMatch) { |
| |
| imgDiv.src = opts[0].imgurl |
| } |
|
|
| |
| select.addEventListener('change', async function () { |
| |
| var selectedOption = select.options[select.selectedIndex].value |
| let t = opts.filter(opt => opt.keyword === selectedOption)[0] |
|
|
| targetWidget.value = await parseImageToBase64(t.imgurl) |
| imgDiv.src = targetWidget.value |
| textWidget.value = t.keyword |
| }) |
| |
| } |
|
|
| app.registerExtension({ |
| name: 'Mixlab.prompt.ImagesPrompt_', |
| async beforeRegisterNodeDef (nodeType, nodeData, app) { |
| if (nodeType.comfyClass == 'ImagesPrompt_') { |
| const orig_nodeCreated = nodeType.prototype.onNodeCreated |
| nodeType.prototype.onNodeCreated = async function () { |
| orig_nodeCreated?.apply(this, arguments) |
|
|
| const image_prompt = this.widgets.filter( |
| w => w.name == 'image_base64' |
| )[0] |
| const image_text = this.widgets.filter(w => w.name == 'text')[0] |
|
|
| const node = this |
|
|
| const widget = { |
| type: 'div', |
| name: 'upload', |
| draw (ctx, node, widget_width, y, widget_height) { |
| Object.assign( |
| this.div.style, |
| get_position_style(ctx, widget_width, y, node.size[1]) |
| ) |
| } |
| } |
|
|
| widget.div = $el('div', {}) |
|
|
| |
| const img = new Image() |
| img.src = image_prompt?.value || base64Df |
| widget.div.appendChild(img) |
|
|
| const btn = document.createElement('button') |
| btn.innerText = 'Upload Images JSON' |
|
|
| btn.style = `cursor: pointer; |
| font-weight: 300; |
| margin: 2px; |
| color: var(--descrip-text); |
| background-color: var(--comfy-input-bg); |
| border-radius: 8px; |
| border-color: var(--border-color); |
| border-style: solid;height: 30px;min-width: 122px; |
| ` |
|
|
| const select = document.createElement('select') |
| select.style = `display:none;cursor: pointer; |
| font-weight: 300; |
| margin: 2px; |
| color: var(--descrip-text); |
| background-color: var(--comfy-input-bg); |
| border-radius: 8px; |
| border-color: var(--border-color); |
| border-style: solid;height: 30px;min-width: 100px; |
| ` |
| widget.select = select |
|
|
| |
| |
| btn.addEventListener('click', () => { |
| let inp = document.createElement('input') |
| inp.type = 'file' |
| inp.accept = '.json' |
| inp.click() |
| inp.addEventListener('change', event => { |
| |
| |
| const file = event.target.files[0] |
| this.title = file.name.split('.')[0] |
|
|
| |
| |
| const reader = new FileReader() |
|
|
| |
| reader.onload = async event => { |
| |
| const json = JSON.parse(event.target.result) |
| console.log(node, json) |
|
|
| widget.value = JSON.stringify(json) |
|
|
| let img = widget.div.querySelector('img') |
|
|
| createSelect(img, select, json, image_prompt, image_text) |
|
|
| image_prompt.value = await parseImageToBase64(json[0].imgurl) |
| image_text.value = json[0].keyword |
|
|
| if (img) { |
| img.src = image_prompt.value |
| } |
|
|
| inp.remove() |
| } |
|
|
| |
| reader.readAsText(file) |
| }) |
| }) |
|
|
| widget.div.appendChild(btn) |
| widget.div.appendChild(select) |
| document.body.appendChild(widget.div) |
| this.addCustomWidget(widget) |
|
|
| const onRemoved = this.onRemoved |
| this.onRemoved = () => { |
| widget.div.remove() |
| return onRemoved?.() |
| } |
|
|
| if (this.onResize) { |
| this.onResize(this.size) |
| } |
|
|
| this.serialize_widgets = true |
| } |
| } |
| }, |
| async loadedGraphNode (node, app) { |
| if (node.type === 'ImagesPrompt_') { |
| try { |
| let prompt = node.widgets.filter(w => w.name === 'image_base64')[0] |
| let text = node.widgets.filter(w => w.name === 'text')[0] |
| let uploadWidget = node.widgets.filter(w => w.name == 'upload')[0] |
| |
| let img = uploadWidget.div.querySelector('img') |
| let json = JSON.parse(uploadWidget.value) |
|
|
| for (let index = 0; index < json.length; index++) { |
| const j = json[index] |
| let base64 = await parseImageToBase64(j.imgurl) |
| if (base64 === prompt.value) { |
| json[index].selected = true |
| } |
| } |
|
|
| if (json && json[0]) { |
| uploadWidget.select.style.display = 'block' |
| createSelect(img, uploadWidget.select, json, prompt, text) |
| } |
| } catch (error) {} |
| } |
| } |
| }) |
|
|
| const createInputImageForBatch = (base64, widget) => { |
| let im = new Image() |
| im.src = base64 |
| im.style = `width: 88px;` |
|
|
| im.addEventListener('click', e => { |
| let newValue = [] |
| let items = widget.value?.base64 || [] |
| for (const v of items) { |
| if (v != base64) newValue.push(v) |
| } |
| widget.value.base64 = newValue |
| im.remove() |
| }) |
|
|
| return im |
| } |
|
|
| app.registerExtension({ |
| name: 'Mixlab.Comfy.LoadImagesToBatch', |
| async getCustomWidgets (app) { |
| return { |
| IMAGEBASE64 (node, inputName, inputData, app) { |
| |
| const widget = { |
| value: { |
| base64: [] |
| }, |
| type: inputData[0], |
| name: inputName, |
| size: [128, 32], |
| draw (ctx, node, width, y) {}, |
| computeSize (...args) { |
| return [128, 32] |
| } |
| |
| |
| |
| } |
| |
| node.addCustomWidget(widget) |
| return widget |
| } |
| } |
| }, |
|
|
| async beforeRegisterNodeDef (nodeType, nodeData, app) { |
| if (nodeType.comfyClass == 'LoadImagesToBatch') { |
|
|
| const orig_nodeCreated = nodeType.prototype.onNodeCreated |
|
|
| nodeType.prototype.onNodeCreated = function () { |
| orig_nodeCreated?.apply(this, arguments) |
|
|
| let imagesWidget = this.widgets.filter(w => w.name == 'images')[0] |
|
|
| const widget = { |
| type: 'div', |
| name: 'image_base64', |
| draw (ctx, node, widget_width, y, widget_height) { |
| Object.assign( |
| this.div.style, |
| get_position_style(ctx, widget_width, 44, node.size[1]) |
| ) |
| }, |
| serialize: false |
| } |
|
|
| widget.div = $el('div', {}) |
|
|
| document.body.appendChild(widget.div) |
|
|
| let imagePreview = document.createElement('div') |
| let imagesDiv = document.createElement('div') |
| imagesDiv.className = 'images_preview' |
| imagesDiv.style = `width: calc(100% - 14px); |
| display: flex; |
| flex-wrap: wrap; |
| padding: 7px; justify-content: space-between; |
| align-items: center;` |
|
|
| let inputImage = document.createElement('input') |
| inputImage.type = 'file' |
| inputImage.style.display = 'none' |
| inputImage.addEventListener('change', e => { |
| e.preventDefault() |
| const file = e.target.files[0] |
| const reader = new FileReader() |
| reader.onload = async event => { |
| let base64 = event.target.result |
| |
| base64 = await loadImageToCanvas(base64) |
| |
| if (!imagesWidget.value) imagesWidget.value = { base64: [] } |
| imagesWidget.value.base64.push(base64) |
| let im = createInputImageForBatch(base64, imagesWidget) |
| imagesDiv.appendChild(im) |
| } |
| reader.readAsDataURL(file) |
| }) |
|
|
| const btn = document.createElement('button') |
| btn.innerText = 'Upload Image' |
|
|
| btn.style = `cursor: pointer; |
| font-weight: 300; |
| margin: 2px; |
| color: var(--descrip-text); |
| background-color: var(--comfy-input-bg); |
| border-radius: 8px; |
| border-color: var(--border-color); |
| border-style: solid;height: 30px;min-width: 122px; |
| ` |
|
|
| btn.addEventListener('click', e => { |
| e.preventDefault() |
| inputImage.click() |
| }) |
|
|
| widget.div.appendChild(imagePreview) |
| imagePreview.appendChild(imagesDiv) |
| imagePreview.appendChild(btn) |
| imagePreview.appendChild(inputImage) |
|
|
| this.addCustomWidget(widget) |
|
|
| |
|
|
| const onRemoved = this.onRemoved |
| this.onRemoved = () => { |
| inputImage.remove() |
| widget.div.remove() |
| try { |
| |
| } catch (error) { |
| console.log(error) |
| } |
|
|
| return onRemoved?.() |
| } |
|
|
| this.serialize_widgets = true |
| } |
| } |
|
|
| if (nodeData.name === 'SaveImageAndMetadata_') { |
| const onNodeCreated = nodeType.prototype.onNodeCreated |
| |
| nodeType.prototype.onNodeCreated = function () { |
| const r = onNodeCreated |
| ? onNodeCreated.apply(this, arguments) |
| : undefined |
| const widget = this.widgets.find(w => w.name === 'filename_prefix') |
| widget.serializeValue = () => { |
| return applyTextReplacements(app, widget.value) |
| } |
|
|
| return r |
| } |
|
|
| const onExecuted = nodeType.prototype.onExecuted |
| nodeType.prototype.onExecuted = function (message) { |
| onExecuted?.apply(this, arguments) |
| console.log('##onExecuted', this, message) |
| |
| if (message.base64) { |
| if (Array.isArray(message.base64)) { |
| } |
| } |
| } |
| } |
| }, |
| async loadedGraphNode (node, app) { |
| if (node.type === 'LoadImagesToBatch') { |
| |
| let imagesWidget = node.widgets.filter(w => w.name === 'images')[0] |
| let imagePreview = node.widgets.filter(w => w.name == 'image_base64')[0] |
|
|
| let pre = imagePreview.div.querySelector('.images_preview') |
| for (const d of imagesWidget.value?.base64 || []) { |
| let im = createInputImageForBatch(d, imagesWidget) |
| pre.appendChild(im) |
| } |
| } |
| } |
| }) |
|
|
| |
| app.registerExtension({ |
| name: 'Mixlab.output.ComparingTwoFrames_', |
| init () { |
| $el('link', { |
| rel: 'stylesheet', |
| href: '/extensions/comfyui-mixlab-nodes/lib/juxtapose.css', |
| parent: document.head |
| }) |
|
|
| $el('style', { |
| textContent: ` |
| .juxtapose-name{ |
| display: none!important; |
| } |
| `, |
| parent: document.body |
| }) |
| }, |
| async beforeRegisterNodeDef (nodeType, nodeData, app) { |
| if (nodeType.comfyClass == 'ComparingTwoFrames_') { |
| const onNodeCreated = nodeType.prototype.onNodeCreated |
| nodeType.prototype.onNodeCreated = function () { |
| const r = onNodeCreated |
| ? onNodeCreated.apply(this, arguments) |
| : undefined |
|
|
| this.size = [400, this.size[1]] |
| console.log('##onNodeCreated', this) |
| const widget = { |
| type: 'div', |
| name: 'preview', |
| draw (ctx, node, widget_width, y, widget_height) { |
| Object.assign( |
| this.div.style, |
| get_position_style(ctx, 400, 44, node.size[1]) |
| ) |
| }, |
| serialize: false |
| } |
|
|
| widget.div = $el('div', {}) |
|
|
| document.body.appendChild(widget.div) |
| this.addCustomWidget(widget) |
| this.serialize_widgets = true |
|
|
|
|
| const onRemoved = this.onRemoved |
| this.onRemoved = () => { |
| widget.div.remove() |
| return onRemoved?.() |
| } |
|
|
|
|
| return r |
|
|
| } |
|
|
| |
|
|
| const onExecuted = nodeType.prototype.onExecuted |
| nodeType.prototype.onExecuted = function (message) { |
| onExecuted?.apply(this, arguments) |
| console.log('##onExecuted', this, message) |
|
|
| this.widgets[0].div.id = 'mix_comparingtowframes_' + this.id |
|
|
| let after_image = message.after_images[0] |
| let before_image = message.before_images[0] |
|
|
| after_image = `${window.location.protocol}//${ |
| window.location.hostname |
| }:${window.location.port}/view?filename=${encodeURIComponent( |
| after_image.filename |
| )}&type=${after_image.type}&subfolder=${encodeURIComponent( |
| after_image.subfolder |
| )}&t=${+new Date()}` |
|
|
| before_image = `${window.location.protocol}//${ |
| window.location.hostname |
| }:${window.location.port}/view?filename=${encodeURIComponent( |
| before_image.filename |
| )}&type=${before_image.type}&subfolder=${encodeURIComponent( |
| before_image.subfolder |
| )}&t=${+new Date()}` |
|
|
| this.widgets[0].div.innerHTML = '' |
|
|
| let slider = new juxtapose.JXSlider( |
| '#mix_comparingtowframes_' + this.id, |
| [ |
| { |
| src: before_image, |
| label: 'Before' |
| }, |
| { |
| src: after_image, |
| label: 'After' |
| } |
| ], |
| { |
| animate: true, |
| showLabels: true, |
| showCredits: false, |
| startingPosition: '50%', |
| makeResponsive: false |
| } |
| ) |
|
|
| this.widgets_values = [ |
| { |
| src: before_image, |
| label: 'Before' |
| }, |
| { |
| src: after_image, |
| label: 'After' |
| } |
| ] |
| this.size=[this.size[0],300] |
| } |
| } |
| }, |
| async loadedGraphNode (node, app) { |
| |
| if (node.type === 'ComparingTwoFrames_') { |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
| } |
| }) |
|
|