| 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) |
| } |
| } |
| }) |
|
|