|
import { app } from '../../../scripts/app.js' |
|
|
|
import { ComfyWidgets } from '../../../scripts/widgets.js' |
|
import { $el } from '../../../scripts/ui.js' |
|
|
|
async function getConfig () { |
|
let api_host = `${window.location.hostname}:${window.location.port}` |
|
let api_base = '' |
|
let url = `${window.location.protocol}//${api_host}${api_base}` |
|
|
|
const res = await fetch(`${url}/mixlab`, { |
|
method: 'POST' |
|
}) |
|
return await res.json() |
|
} |
|
|
|
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 |
|
} |
|
|
|
app.registerExtension({ |
|
name: 'Mixlab.GPT.ChatGPTOpenAI', |
|
async getCustomWidgets (app) { |
|
return { |
|
KEY (node, inputName, inputData, app) { |
|
|
|
const widget = { |
|
type: inputData[0], |
|
name: inputName, |
|
size: [128, 32], |
|
draw (ctx, node, width, y) {}, |
|
computeSize (...args) { |
|
return [128, 32] |
|
}, |
|
async serializeValue (nodeId, widgetIndex) { |
|
let data = getLocalData('_mixlab_api_key') |
|
return data[node.id] || 'by Mixlab' |
|
} |
|
} |
|
|
|
node.addCustomWidget(widget) |
|
return widget |
|
}, |
|
URL (node, inputName, inputData, app) { |
|
|
|
const widget = { |
|
type: inputData[0], |
|
name: inputName, |
|
size: [128, 32], |
|
draw (ctx, node, width, y) { |
|
|
|
}, |
|
computeSize (...args) { |
|
return [128, 32] |
|
}, |
|
async serializeValue (nodeId, widgetIndex) { |
|
let data = getLocalData('_mixlab_api_url') |
|
return data[node.id] || 'https://api.openai.com/v1' |
|
} |
|
} |
|
|
|
node.addCustomWidget(widget) |
|
return widget |
|
} |
|
} |
|
}, |
|
|
|
async beforeRegisterNodeDef (nodeType, nodeData, app) { |
|
if (nodeType.comfyClass == 'ChatGPTOpenAI') { |
|
const orig_nodeCreated = nodeType.prototype.onNodeCreated |
|
nodeType.prototype.onNodeCreated = function () { |
|
orig_nodeCreated?.apply(this, arguments) |
|
|
|
const api_key = this.widgets.filter(w => w.name == 'api_key')[0] |
|
const api_url = this.widgets.filter(w => w.name == 'api_url')[0] |
|
|
|
console.log('ChatGPTOpenAI nodeData', this.widgets) |
|
|
|
const widget = { |
|
type: 'div', |
|
name: 'chatgptdiv', |
|
draw (ctx, node, widget_width, y, widget_height) { |
|
Object.assign( |
|
this.div.style, |
|
get_position_style(ctx, widget_width, api_key.y, node.size[1]) |
|
) |
|
} |
|
} |
|
|
|
widget.div = $el('div', {}) |
|
|
|
document.body.appendChild(widget.div) |
|
|
|
const inputDiv = (key, placeholder) => { |
|
let div = document.createElement('div') |
|
const ip = document.createElement('input') |
|
ip.type = placeholder === 'Key' ? 'password' : 'text' |
|
ip.className = `${'comfy-multiline-input'} ${placeholder}` |
|
div.style = `display: flex; |
|
align-items: center; |
|
margin: 6px 8px; |
|
margin-top: 0;` |
|
ip.placeholder = placeholder |
|
ip.value = placeholder |
|
|
|
ip.style = `margin-left: 24px; |
|
outline: none; |
|
border: none; |
|
padding: 4px;width: 100%;` |
|
const label = document.createElement('label') |
|
label.style = 'font-size: 10px;min-width:32px' |
|
label.innerText = placeholder |
|
div.appendChild(label) |
|
div.appendChild(ip) |
|
|
|
ip.addEventListener('change', () => { |
|
let data = getLocalData(key) |
|
data[this.id] = ip.value.trim() |
|
localStorage.setItem(key, JSON.stringify(data)) |
|
console.log(this.id, key) |
|
}) |
|
return div |
|
} |
|
|
|
let inputKey = inputDiv('_mixlab_api_key', 'Key') |
|
let inputUrl = inputDiv('_mixlab_api_url', 'URL') |
|
|
|
widget.div.appendChild(inputKey) |
|
widget.div.appendChild(inputUrl) |
|
|
|
this.addCustomWidget(widget) |
|
|
|
const onRemoved = this.onRemoved |
|
this.onRemoved = () => { |
|
inputUrl.remove() |
|
inputKey.remove() |
|
widget.div.remove() |
|
return onRemoved?.() |
|
} |
|
|
|
this.serialize_widgets = true |
|
} |
|
} |
|
}, |
|
async loadedGraphNode (node, app) { |
|
|
|
|
|
|
|
if (node.type === 'ChatGPTOpenAI') { |
|
let widget = node.widgets.filter(w => w.div)[0] |
|
|
|
let apiKey = getLocalData('_mixlab_api_key'), |
|
url = getLocalData('_mixlab_api_url') |
|
|
|
let id = node.id |
|
|
|
|
|
|
|
widget.div.querySelector('.Key').value = apiKey[id] || 'by Mixlab' |
|
widget.div.querySelector('.URL').value = |
|
url[id] || 'https://api.openai.com/v1' |
|
} |
|
} |
|
}) |
|
|
|
app.registerExtension({ |
|
name: 'Mixlab.GPT.ShowTextForGPT', |
|
async beforeRegisterNodeDef (nodeType, nodeData, app) { |
|
if (nodeData.name === 'ShowTextForGPT') { |
|
function populate (text) { |
|
text = text.filter(t => t && t?.trim()) |
|
|
|
if (this.widgets) { |
|
|
|
|
|
for (let i = 0; i < this.widgets.length; i++) { |
|
if (this.widgets[i].name == 'show_text') |
|
this.widgets[i].onRemove?.() |
|
console.log('#ShowTextForGPT', this.widgets[i]) |
|
} |
|
this.widgets.length = 2 |
|
} |
|
|
|
for (let list of text) { |
|
if (list) { |
|
|
|
const w = ComfyWidgets['STRING']( |
|
this, |
|
'show_text', |
|
['STRING', { multiline: true }], |
|
app |
|
).widget |
|
w.inputEl.readOnly = true |
|
w.inputEl.style.opacity = 0.6 |
|
|
|
|
|
|
|
try { |
|
if (typeof list != 'string') { |
|
let data = JSON.parse(list) |
|
data = Array.from(data, d => { |
|
return { |
|
...d, |
|
content: decodeURIComponent(d.content) |
|
} |
|
}) |
|
list = JSON.stringify(data, null, 2) |
|
} |
|
} catch (error) { |
|
console.log(error) |
|
} |
|
|
|
w.value = list |
|
} |
|
} |
|
|
|
requestAnimationFrame(() => { |
|
if (this) { |
|
const sz = this.computeSize() |
|
if (sz[0] < this.size[0]) { |
|
sz[0] = this.size[0] |
|
} |
|
if (sz[1] < this.size[1]) { |
|
sz[1] = this.size[1] |
|
} |
|
this.onResize?.(sz) |
|
app.graph.setDirtyCanvas(true, false) |
|
} |
|
}) |
|
} |
|
|
|
|
|
const onExecuted = nodeType.prototype.onExecuted |
|
nodeType.prototype.onExecuted = function (message) { |
|
onExecuted?.apply(this, arguments) |
|
|
|
if (message.text) populate.call(this, message.text) |
|
} |
|
|
|
const onConfigure = nodeType.prototype.onConfigure |
|
nodeType.prototype.onConfigure = function () { |
|
onConfigure?.apply(this, arguments) |
|
if (this.widgets_values?.length) { |
|
populate.call(this, this.widgets_values) |
|
} |
|
} |
|
|
|
this.serialize_widgets = true |
|
} |
|
}, |
|
async loadedGraphNode (node, app) { |
|
if (node.type === 'ShowTextForGPT') { |
|
let widget = node.widgets.filter(w => w.name == 'show_text')[0] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('#loadedGraphNode', node) |
|
} |
|
} |
|
}) |
|
|