Files

1034 lines
42 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// WeiLin Prompt UI Node - JavaScript Extension
// ComfyUI 2.0兼容性:从window.comfyAPI获取app对象
console.log('[WeiLin] JavaScript file loaded: weilin_prompt_ui_node.js');
// 加载CSS修复文件
(function() {
// 尝试多个可能的路径
const possiblePaths = [
'./extensions/weilin-comfyui-tools/weilin_fix.css',
'./extensions/weilin-comfyui-tools/js_node/weilin_fix.css',
'./weilin_fix.css'
];
possiblePaths.forEach(path => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = path;
document.head.appendChild(link);
});
console.log('[WeiLin] CSS fix files loaded');
})();
// 防止重复注册
if (window.weilinExtensionRegistered) {
console.log('[WeiLin] Extension already registered, skipping...');
} else {
window.weilinExtensionRegistered = true;
// 等待app对象可用
function waitForApp(callback, maxAttempts = 50) {
let attempts = 0;
const check = () => {
attempts++;
// ComfyUI 2.0: 从window.comfyAPI获取app对象
const app = window.comfyAPI?.app?.app || window.app || window.comfyApp;
if (app) {
console.log('[WeiLin] App object found:', app);
callback(app);
} else if (attempts < maxAttempts) {
setTimeout(check, 100);
} else {
console.error('[WeiLin] Failed to find app object after', maxAttempts, 'attempts');
// console.log('[WeiLin] window.comfyAPI:', window.comfyAPI);
// console.log('[WeiLin] window.app:', window.app);
// console.log('[WeiLin] window.comfyApp:', window.comfyApp);
}
};
check();
}
// 提示词 Node
// localStorage.setItem("weilin_prompt_ui_onfirst", 0);
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
let localLanguage = "打开提示词编辑器"
let localOpenLoraLanguage = "打开Lora堆"
function getBrowserLanguage() {
// 获取浏览器语言
const language = navigator.language || navigator.userLanguage;
// 判断语言类型
if (language.startsWith('zh')) {
localLanguage = "打开提示词编辑器"
localOpenLoraLanguage = "打开Lora堆"
} else if (language.startsWith('en')) {
localLanguage = "Open Prompt UI"
localOpenLoraLanguage = "Open Lora Stack"
} else {
localLanguage = "Open Prompt UI"
localOpenLoraLanguage = "Open Lora Stack"
}
}
let globalNodeList = []
let global_randomID = generateUUID(); // 随机种子ID
function updateNodeTitleBySeed(seed, newTitle) {
// 使用 find 方法查找目标节点
const targetNode = globalNodeList.find(node => node.seed === seed);
if (targetNode) {
// 如果找到目标节点,修改其 title
targetNode.title = newTitle;
}
}
function updateNodeIdBySeed(seed, newId) {
const targetNode = globalNodeList.find(node => node.seed === seed);
if (targetNode) {
targetNode.id = newId;
}
}
function updateNodeTextBySeed(seed, newText) {
const targetNode = globalNodeList.find(node => node.seed === seed);
if (targetNode) {
targetNode.text = newText;
}
}
// 根据seed删除元素
function removeNodeBySeed(seed) {
const index = globalNodeList.findIndex(node => node.seed === seed);
if (index !== -1) {
globalNodeList.splice(index, 1);
}
}
// 版本号,用于强制刷新缓存 - 修改此值可强制浏览器重新加载静态资源
const WEILIN_VERSION = '1.0.1';
// 资源加载状态
let resourcesLoaded = false;
let resourcesLoading = false;
// 按需加载资源 - 只在用户首次打开编辑器时才加载
function loadResourcesOnDemand() {
// 如果资源已加载或正在加载,直接返回
if (resourcesLoaded || resourcesLoading) return Promise.resolve();
resourcesLoading = true;
return new Promise((resolve) => {
let loadedCount = 0;
const totalResources = 4;
const checkAllLoaded = () => {
loadedCount++;
if (loadedCount === totalResources) {
// 等待Vue应用初始化完成
waitForVueAppInit(resolve);
}
};
// 等待Vue应用初始化完成的函数
const waitForVueAppInit = (resolveCallback) => {
// 检查Vue应用是否已初始化的标志:
// 1. weilin_comfyui_tools_prompt_ui_div 元素存在
// 2. Vue应用已挂载(检查 __vue_app__ 或元素内部有内容)
const checkVueReady = () => {
const container = document.getElementById('weilin_comfyui_tools_prompt_ui_div');
if (!container) return false;
// Vue 3 会在挂载元素上设置 __vue_app__ 属性
// 或者检查容器内是否有Vue渲染的内容
const hasVueApp = container.__vue_app__ ||
container.__vue__ ||
container.children.length > 0;
if (hasVueApp) {
console.log('[WeiLin] Vue app initialized, resources ready');
resourcesLoaded = true;
resourcesLoading = false;
resolveCallback();
return true;
}
return false;
};
// 立即检查一次
if (checkVueReady()) return;
// 如果还没准备好,使用轮询检查
let attempts = 0;
const maxAttempts = 100; // 最多等待10秒
const pollInterval = setInterval(() => {
attempts++;
if (checkVueReady()) {
clearInterval(pollInterval);
} else if (attempts >= maxAttempts) {
console.warn('[WeiLin] Vue app initialization timeout, proceeding anyway');
clearInterval(pollInterval);
resourcesLoaded = true;
resourcesLoading = false;
resolveCallback();
}
}, 100);
};
// 加载主JS (648KB) - 移除defer,让脚本立即执行
var script1 = document.createElement('script');
script1.src = './weilin/prompt_ui/webjs?v=' + WEILIN_VERSION;
script1.type = 'text/javascript';
script1.onload = checkAllLoaded;
script1.onerror = checkAllLoaded;
document.head.appendChild(script1);
// 加载CSS - 使用preload优化
var link1 = document.createElement('link');
link1.rel = 'stylesheet';
link1.type = 'text/css';
link1.href = './weilin/prompt_ui/file/style.css?v=' + WEILIN_VERSION;
link1.onload = checkAllLoaded;
link1.onerror = checkAllLoaded;
document.head.appendChild(link1);
// loraStack 脚本载入
var script2 = document.createElement('script');
script2.src = './weilin/prompt_ui/file/lora_stack.js?v=' + WEILIN_VERSION;
script2.type = 'text/javascript';
script2.onload = checkAllLoaded;
script2.onerror = checkAllLoaded;
document.head.appendChild(script2);
// loraStack CSS
var link2 = document.createElement('link');
link2.rel = 'stylesheet';
link2.type = 'text/css';
link2.href = './weilin/prompt_ui/file/lora_stack.css?v=' + WEILIN_VERSION;
link2.onload = checkAllLoaded;
link2.onerror = checkAllLoaded;
document.head.appendChild(link2);
});
}
// 不再自动加载资源,改为按需加载
// setTimeout(initWindow, 2000);
// 等待app对象可用后注册扩展
waitForApp((app) => {
console.log('[WeiLin] Registering extension...');
app.registerExtension({
name: "weilin.prompt_ui_node",
async init() {
console.log('[WeiLin] Extension init');
// 检查ComfyUI是否识别了WeiLin节点
setTimeout(() => {
console.log('[WeiLin] Checking if WeiLin nodes are registered...');
// 尝试获取所有已注册的节点
if (window.comfyAPI && window.comfyAPI.app && window.comfyAPI.app.app) {
const app = window.comfyAPI.app.app;
// console.log('[WeiLin] App object:', app);
// 检查nodes属性
if (app.nodes) {
console.log('[WeiLin] Registered nodes:', Object.keys(app.nodes));
// 查找WeiLin节点
const weilinNodes = Object.keys(app.nodes).filter(name => name.includes('WeiLin'));
// console.log('[WeiLin] WeiLin nodes found:', weilinNodes);
}
}
}, 2000);
},
async setup() {
console.log('[WeiLin] Extension setup');
},
async beforeRegisterNodeDef(nodeType, nodeData, app) {
// console.log('[WeiLin] beforeRegisterNodeDef called, nodeData.name:', nodeData.name);
// 检查是否是WeiLin节点
if (
nodeData.name === "WeiLinPromptUI" ||
nodeData.name === "WeiLinPromptUIWithoutLora" ||
nodeData.name === "WeiLinPromptUIOnlyLoraStack"
) {
console.log('[WeiLin] ⭐ Matching node found:', nodeData.name);
console.log('[WeiLin] Node data:', nodeData);
console.log('[WeiLin] Node type:', nodeType);
}
// console.log(app)
if (
nodeData.name === "WeiLinPromptUI" ||
nodeData.name === "WeiLinPromptUIWithoutLora" ||
nodeData.name === "WeiLinPromptUIOnlyLoraStack"
) {
console.log('[WeiLin] Matching node found:', nodeData.name);
// Create node
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = async function () {
console.log('[WeiLin] onNodeCreated called for:', nodeData.name);
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
const thisNodeName = nodeData.name // 存储当前的节点名称
let nodeTextAreaList = [] // 按顺序载入element,name="positive" || "lora_str" || "temp_str"
let nodeWidgetList = [] // 保存widget引用,用于同步更新widget.value
const thisNodeSeed = generateUUID(); // 随机唯一种子ID
// 清理可能残留的遮罩层
const cleanupOverlays = () => {
// 清理loading遮罩层
const overlays = document.querySelectorAll('.loading-overlay, .weilin-comfyui-loading-overlay');
overlays.forEach(overlay => {
// 只移除不在DOM树中的孤立遮罩层
if (!overlay.closest('.weilin-prompt-ui-container') && !overlay.closest('.weilin-lora-manager-container')) {
overlay.remove();
}
});
};
cleanupOverlays();
// 延迟再次清理,确保所有DOM操作完成
setTimeout(cleanupOverlays, 100);
setTimeout(cleanupOverlays, 500);
// ========================================
// 核心修复:解决dom-widget容器遮挡画布的问题
// ========================================
// 问题:dom-widget容器有 position:fixed 和 size-full,遮挡整个画布
// 解决:只修复当前节点的dom-widget,不影响其他节点
// ========================================
const fixCurrentNodeDomWidgets = () => {
if (!this.widgets) return;
const isWeiLinNode =
nodeData.name === "WeiLinPromptUI" ||
nodeData.name === "WeiLinPromptUIWithoutLora" ||
nodeData.name === "WeiLinPromptUIOnlyLoraStack";
const processedDomWidgets = new Set();
this.widgets.forEach(widget => {
if (widget.element) {
// 找到widget的dom-widget容器
let parent = widget.element.parentElement;
while (parent) {
if (parent.classList && parent.classList.contains('dom-widget')) {
if (processedDomWidgets.has(parent)) {
parent = parent.parentElement;
continue;
}
processedDomWidgets.add(parent);
const isFirstFix = !parent.classList.contains('weilin-owned-dom-widget')
&& !parent.classList.contains('weilin-owned-dom-widget-fixed');
parent.style.setProperty('pointer-events', 'none', 'important');
// WeiLin 节点保留 Comfy 原生缩放链(position:fixed + size-full),
// 仅修复 pointer-events 以消除透明遮罩
if (isWeiLinNode) {
parent.classList.add('weilin-owned-dom-widget-fixed');
} else {
parent.classList.add('weilin-owned-dom-widget');
parent.style.setProperty('position', 'absolute', 'important');
parent.classList.remove('size-full');
}
if (isFirstFix) {
console.log('[WeiLin] Fixed dom-widget for node:', nodeData.name);
}
// 确保内部元素可以交互
if (widget.element) {
widget.element.style.setProperty('pointer-events', 'auto', 'important');
}
break;
}
parent = parent.parentElement;
}
}
});
};
// 立即执行修复
fixCurrentNodeDomWidgets();
// 延迟执行,确保DOM完全加载
setTimeout(fixCurrentNodeDomWidgets, 100);
setTimeout(fixCurrentNodeDomWidgets, 500);
setTimeout(fixCurrentNodeDomWidgets, 1000);
if (nodeData.name === "WeiLinPromptUI" || nodeData.name === "WeiLinPromptUIWithoutLora") {
hideWidgetForGood(this, this.widgets.find(w => w.name === "temp_str"))
hideWidgetForGood(this, this.widgets.find(w => w.name === "random_template"))
}
if (nodeData.name === "WeiLinPromptUI" || nodeData.name === "WeiLinPromptUIOnlyLoraStack") {
hideWidgetForGood(this, this.widgets.find(w => w.name === "lora_str"))
hideWidgetForGood(this, this.widgets.find(w => w.name === "temp_lora_str"))
}
for (let index = 0; index < this.widgets.length; index++) {
const widgetItem = this.widgets[index];
if (widgetItem.name == "positive") {
let thisInputElement = widgetItem.element
// thisInputElement.readOnly = true
nodeTextAreaList[0] = thisInputElement
nodeWidgetList[0] = widgetItem
} else if (widgetItem.name == "lora_str") {
let thisInputElement = widgetItem.element
thisInputElement.readOnly = true
nodeTextAreaList[1] = thisInputElement
nodeWidgetList[1] = widgetItem
} else if (widgetItem.name == "temp_str") {
let thisInputElement = widgetItem.element
thisInputElement.readOnly = true
nodeTextAreaList[2] = thisInputElement
nodeWidgetList[2] = widgetItem
} else if (widgetItem.name == "temp_lora_str") {
let thisInputElement = widgetItem.element
thisInputElement.readOnly = true
nodeTextAreaList[3] = thisInputElement
nodeWidgetList[3] = widgetItem
} else if (widgetItem.name == "random_template") {
let thisInputElement = widgetItem.element
thisInputElement.readOnly = true
nodeTextAreaList[4] = thisInputElement
nodeWidgetList[4] = widgetItem
}
}
// 为不同的按钮使用不同的ID,避免冲突(必须在使用前声明)
// 修复Bug:初始化时直接生成唯一UUID,防止被全局窗口(空ID)广播的消息意外覆盖
let promptBoxRandomID = generateUUID();
let loraStackRandomID = generateUUID();
// 监听lora数据变化,通知UI窗口同步
if (nodeData.name === "WeiLinPromptUI" || nodeData.name === "WeiLinPromptUIOnlyLoraStack") {
// 监听 lora_str 变化
if (nodeTextAreaList[1]) {
const loraTextarea = nodeTextAreaList[1];
const originalLoraValue = loraTextarea.value;
// 使用 MutationObserver 监听值变化
const loraObserver = new MutationObserver(() => {
if (loraTextarea.value !== originalLoraValue) {
notifyLoraDataChange();
}
});
// 同时监听 input 事件
loraTextarea.addEventListener('input', () => {
notifyLoraDataChange();
});
// 监听 value 属性变化
let currentLoraValue = loraTextarea.value;
Object.defineProperty(loraTextarea, 'value', {
get() {
return currentLoraValue;
},
set(newValue) {
currentLoraValue = newValue;
notifyLoraDataChange();
},
enumerable: true,
configurable: true
});
}
// 监听 temp_lora_str 变化
if (nodeTextAreaList[3]) {
const tempLoraTextarea = nodeTextAreaList[3];
const originalTempLoraValue = tempLoraTextarea.value;
// 监听 input 事件
tempLoraTextarea.addEventListener('input', () => {
notifyLoraDataChange();
});
// 监听 value 属性变化
let currentTempLoraValue = tempLoraTextarea.value;
Object.defineProperty(tempLoraTextarea, 'value', {
get() {
return currentTempLoraValue;
},
set(newValue) {
currentTempLoraValue = newValue;
notifyLoraDataChange();
},
enumerable: true,
configurable: true
});
}
}
// 通知UI窗口lora数据变化的函数
function notifyLoraDataChange() {
if (promptBoxRandomID) {
let jsonData = {
prompt: nodeTextAreaList[0] ? nodeTextAreaList[0].value : "",
lora: [],
temp_prompt: {},
temp_lora: {},
}
if (nodeData.name === "WeiLinPromptUI" && nodeTextAreaList[1] && nodeTextAreaList[1].value && nodeTextAreaList[1].value.length > 0) {
try {
jsonData.lora = JSON.parse(nodeTextAreaList[1].value);
} catch (e) {}
}
if (nodeTextAreaList[2] && nodeTextAreaList[2].value && nodeTextAreaList[2].value.length > 0) {
try {
jsonData.temp_prompt = JSON.parse(nodeTextAreaList[2].value)
} catch (e) {}
}
if ((nodeData.name === "WeiLinPromptUI" || nodeData.name === "WeiLinPromptUIOnlyLoraStack") && nodeTextAreaList[3] && nodeTextAreaList[3].value && nodeTextAreaList[3].value.length > 0) {
try {
jsonData.temp_lora = JSON.parse(nodeTextAreaList[3].value)
} catch (e) {}
}
window.postMessage({
type: 'weilin_prompt_ui_lora_data_changed_' + promptBoxRandomID,
data: JSON.stringify(jsonData)
}, '*')
}
}
// Lora Stack 创建可视化节点
if (nodeData.name === "WeiLinPromptUIOnlyLoraStack") {
// 确保资源加载完成后再创建widget
await loadResourcesOnDemand();
await createLoraStackWidget(this, thisNodeSeed,nodeTextAreaList[3]);
}
// console.log(this)
// 修改的是这部分
if (nodeData.name === "WeiLinPromptUI" ||
nodeData.name === "WeiLinPromptUIWithoutLora") {
globalNodeList.push({ seed: thisNodeSeed, text: nodeTextAreaList[0].value, id: this.id })
const textarea = nodeTextAreaList[0];
textarea.addEventListener('input', (event) => {
const newValue = event.target.value;
// 修复Bug:补充缺失的 thisNodeSeed 参数
updateNodeTextBySeed(thisNodeSeed, newValue);
window.parent.postMessage({ type: 'weilin_prompt_ui_update_node_list_info', nodeList: globalNodeList }, '*')
});
}
// 监听节点ID
let currentThisId = this.id
Object.defineProperty(this, 'id', {
get() {
return currentThisId;
},
set(newValue) {
currentThisId = newValue;
onTisIdChange(newValue);
},
enumerable: true,
configurable: true
});
function onTisIdChange(newId) {
// console.log(newId)
if (nodeData.name === "WeiLinPromptUI" ||
nodeData.name === "WeiLinPromptUIWithoutLora") {
updateNodeIdBySeed(thisNodeSeed, newId);
window.parent.postMessage({ type: 'weilin_prompt_ui_update_node_list_info', nodeList: globalNodeList }, '*')
}
}
// 监听 this.title 的变化
let currentTitle = this.title; // 缓存当前值
Object.defineProperty(this, 'title', {
get() {
return currentTitle;
},
set(newValue) {
// console.log(`this.title changed from ${currentTitle} to ${newValue}`);
currentTitle = newValue;
// 触发回调,返回新的 this.title 数据
onTitleChange(newValue);
},
enumerable: true,
configurable: true
});
// 监听 this.title 变化的回调函数
function onTitleChange(newTitle) {
// console.log("New this.title:", newTitle);
// 在这里可以处理新的 this.title 数据
// 例如,将新的 this.title 传递给其他逻辑
if (nodeData.name === "WeiLinPromptUI" ||
nodeData.name === "WeiLinPromptUIWithoutLora") {
updateNodeTitleBySeed(thisNodeSeed, newTitle);
window.parent.postMessage({ type: 'weilin_prompt_ui_update_node_list_info', nodeList: globalNodeList }, '*')
}
}
// console.log(thisNodeSeed)
//console.log(globalNodeList)
// 定义消息处理函数,保存引用以便后续移除
const messageHandler = (event) => {
// console.log(e)
if (event.data.type === 'weilin_prompt_ui_prompt_update_prompt_' + promptBoxRandomID) {
// 接收到更新提示词内容消息
const jsonReponse = JSON.parse(event.data.data)
// console.log(jsonReponse)
nodeTextAreaList[0].value = jsonReponse.prompt;
if (nodeWidgetList[0]) nodeWidgetList[0].value = jsonReponse.prompt;
if (nodeData.name === "WeiLinPromptUI") {
// console.log(jsonReponse.lora.length)
if (jsonReponse.lora && Array.isArray(jsonReponse.lora) && jsonReponse.lora.length > 0) {
if (nodeTextAreaList[1]) nodeTextAreaList[1].value = JSON.stringify(jsonReponse.lora);
if (nodeWidgetList[1]) nodeWidgetList[1].value = JSON.stringify(jsonReponse.lora);
} else {
if (nodeTextAreaList[1]) nodeTextAreaList[1].value = "";
if (nodeWidgetList[1]) nodeWidgetList[1].value = "";
}
}
if (jsonReponse.temp_prompt && (typeof jsonReponse.temp_prompt === 'object') && Object.keys(jsonReponse.temp_prompt).length > 0) {
if (nodeTextAreaList[2]) nodeTextAreaList[2].value = JSON.stringify(jsonReponse.temp_prompt);
if (nodeWidgetList[2]) nodeWidgetList[2].value = JSON.stringify(jsonReponse.temp_prompt);
}else {
if (nodeTextAreaList[2]) nodeTextAreaList[2].value = "";
if (nodeWidgetList[2]) nodeWidgetList[2].value = "";
}
if (nodeData.name === "WeiLinPromptUI") {
if (jsonReponse.temp_lora && Array.isArray(jsonReponse.temp_lora)) {
if (nodeTextAreaList[3]) nodeTextAreaList[3].value = JSON.stringify(jsonReponse.temp_lora);
if (nodeWidgetList[3]) nodeWidgetList[3].value = JSON.stringify(jsonReponse.temp_lora);
}else {
if (nodeTextAreaList[3]) nodeTextAreaList[3].value = "";
if (nodeWidgetList[3]) nodeWidgetList[3].value = "";
}
}
// console.log(nodeTextAreaList)
updateNodeTextBySeed(thisNodeSeed, jsonReponse.prompt);
window.parent.postMessage({ type: 'weilin_prompt_ui_update_node_list_info', nodeList: globalNodeList }, '*')
} else if (event.data.type === 'weilin_prompt_ui_prompt_get_node_list_info') {
// 获取节点导航信息
if (nodeData.name === "WeiLinPromptUI" || nodeData.name === "WeiLinPromptUIWithoutLora") {
updateNodeTextBySeed(thisNodeSeed, nodeTextAreaList[0].value);
window.parent.postMessage({ type: 'weilin_prompt_ui_update_node_list_info', nodeList: globalNodeList }, '*')
}
} else if (event.data.type === "weilin_prompt_ui_prompt_open_node_wit_seed" && event.data.seed === thisNodeSeed) {
// 节点导航打开节点UI按钮
// 先加载资源
loadResourcesOnDemand().then(() => {
promptBoxRandomID = generateUUID();
// console.log("register====>",promptBoxRandomID)
let jsonData = {
prompt: nodeTextAreaList[0].value,
lora: [],
temp_prompt: {},
temp_lora: {},
}
if (nodeData.name === "WeiLinPromptUI" && nodeTextAreaList[1] && nodeTextAreaList[1].value && nodeTextAreaList[1].value.length > 0) {
jsonData.lora = JSON.parse(nodeTextAreaList[1].value);
}
if (nodeTextAreaList[2] && nodeTextAreaList[2].value && nodeTextAreaList[2].value.length > 0) {
jsonData.temp_prompt = JSON.parse(nodeTextAreaList[2].value)
}
if (nodeData.name === "WeiLinPromptUI" && nodeTextAreaList[3] && nodeTextAreaList[3].value && nodeTextAreaList[3].value.length > 0) {
jsonData.temp_lora = JSON.parse(nodeTextAreaList[3].value)
}
const data = JSON.stringify(jsonData)
window.parent.postMessage({ type: 'weilin_prompt_ui_openPromptBox', id: promptBoxRandomID, prompt: data, node: nodeData.name }, '*')
});
} else if (event.data.type === 'weilin_prompt_ui_prompt_finish_lora_stack_' + promptBoxRandomID) {
// 接收到更新LoraStack内容消息
const jsonReponse = JSON.parse(event.data.data)
// console.log(jsonReponse)
if (nodeData.name === "WeiLinPromptUI" || nodeData.name === "WeiLinPromptUIOnlyLoraStack") {
// console.log(jsonReponse.lora.length)
if (jsonReponse.lora && Array.isArray(jsonReponse.lora) && jsonReponse.lora.length > 0) {
if (nodeTextAreaList[1]) nodeTextAreaList[1].value = JSON.stringify(jsonReponse.lora);
if (nodeWidgetList[1]) nodeWidgetList[1].value = JSON.stringify(jsonReponse.lora);
} else {
if (nodeTextAreaList[1]) nodeTextAreaList[1].value = "";
if (nodeWidgetList[1]) nodeWidgetList[1].value = "";
}
if (jsonReponse.temp_lora && Array.isArray(jsonReponse.temp_lora)) {
if (nodeTextAreaList[3]) nodeTextAreaList[3].value = JSON.stringify(jsonReponse.temp_lora);
if (nodeWidgetList[3]) nodeWidgetList[3].value = JSON.stringify(jsonReponse.temp_lora);
}else{
if (nodeTextAreaList[3]) nodeTextAreaList[3].value = "";
if (nodeWidgetList[3]) nodeWidgetList[3].value = "";
}
if (nodeTextAreaList[3] && nodeTextAreaList[3].value && nodeTextAreaList[3].value.length > 0) {
window.weilinGlobalSelectedLoras[thisNodeSeed] = JSON.parse(nodeTextAreaList[3].value)
}else {
window.weilinGlobalSelectedLoras[thisNodeSeed]= []
}
renderAllLoras(thisNodeSeed)
}
}else if (event.data.type === "weilin_prompt_ui_prompt_node_finish_lora_stack_" + thisNodeSeed) {
// 接收到更新LoraStack内容消息
const jsonReponse = JSON.parse(event.data.data)
if (nodeData.name === "WeiLinPromptUI" || nodeData.name === "WeiLinPromptUIOnlyLoraStack") {
// 处理 lora 数据
if (jsonReponse.lora && Array.isArray(jsonReponse.lora) && jsonReponse.lora.length > 0) {
if (nodeTextAreaList[1]) nodeTextAreaList[1].value = JSON.stringify(jsonReponse.lora);
if (nodeWidgetList[1]) nodeWidgetList[1].value = JSON.stringify(jsonReponse.lora);
} else {
if (nodeTextAreaList[1]) nodeTextAreaList[1].value = "";
if (nodeWidgetList[1]) nodeWidgetList[1].value = "";
}
// 处理 temp_lora 数据 - 支持空数组
if (jsonReponse.temp_lora && Array.isArray(jsonReponse.temp_lora)) {
if (nodeTextAreaList[3]) nodeTextAreaList[3].value = JSON.stringify(jsonReponse.temp_lora);
if (nodeWidgetList[3]) nodeWidgetList[3].value = JSON.stringify(jsonReponse.temp_lora);
// 更新全局数据
window.weilinGlobalSelectedLoras[thisNodeSeed] = jsonReponse.temp_lora;
} else {
if (nodeTextAreaList[3]) nodeTextAreaList[3].value = "";
if (nodeWidgetList[3]) nodeWidgetList[3].value = "";
window.weilinGlobalSelectedLoras[thisNodeSeed] = [];
}
// 重新渲染节点上的Lora堆
renderAllLoras(thisNodeSeed)
// 广播消息给所有组件(包括内嵌Lora堆和独立窗口)
updateLoraStackInfoToWindows(thisNodeSeed)
}
}else if (event.data.type === `weilin_prompt_ui_update_lora_tags_${thisNodeSeed}`) {
// 处理来自独立窗口的Lora标签更新消息
const loraTags = event.data.tags
const currentPrompt = nodeTextAreaList[0].value
// 使用与编辑器相同的逻辑处理提示词文本
if (loraTags && loraTags.length > 0) {
// 移除所有现有的 <wlr:...> 标签
const wlrPattern = /<wlr:[^>]+>/g
let cleanText = currentPrompt.replace(wlrPattern, '')
// 清理连续的逗号和空格
cleanText = cleanText
.replace(/,\s*,/g, ',') // 连续逗号替换为单个逗号
.replace(/,\s*$/g, '') // 移除末尾的逗号和空格
.replace(/^\s*,/g, '') // 移除开头的逗号和空格
.trim()
// 添加新的标签到开头
const newTags = loraTags.join(', ')
if (cleanText) {
nodeTextAreaList[0].value = `${newTags}, ${cleanText}`
} else {
nodeTextAreaList[0].value = newTags
}
} else {
// 当 loraTags 为空数组时,清空提示词中的所有 LoRA 标签
const wlrPattern = /<wlr:[^>]+>/g
let cleanText = currentPrompt.replace(wlrPattern, '')
// 清理连续的逗号和空格
cleanText = cleanText
.replace(/,\s*,/g, ',')
.replace(/,\s*$/g, '')
.replace(/^\s*,/g, '')
.trim()
nodeTextAreaList[0].value = cleanText
}
// 同步更新Widget
if (nodeWidgetList[0]) nodeWidgetList[0].value = nodeTextAreaList[0].value
}else if (event.data.type === "weilin_prompt_ui_query_lora_stack_" + thisNodeSeed) {
// 查询Lora堆数据 - 组件打开时主动查询
if (nodeData.name === "WeiLinPromptUI" || nodeData.name === "WeiLinPromptUIOnlyLoraStack") {
const currentLoras = window.weilinGlobalSelectedLoras[thisNodeSeed] || [];
const putJson = {
lora: currentLoras.filter(lora => !lora.hidden),
temp_lora: currentLoras
};
window.postMessage({
type: 'weilin_prompt_ui_query_lora_stack_response_' + thisNodeSeed,
data: JSON.stringify(putJson)
}, '*');
}
}else if (event.data.type === "weilin_prompt_ui_selectLora_stack_node_"+thisNodeSeed) {
addLora(thisNodeSeed,event.data.lora)
}else if (event.data.type === "weilin_prompt_ui_update_template_"+promptBoxRandomID) {
nodeTextAreaList[4].value = event.data.data
if (nodeWidgetList[4]) nodeWidgetList[4].value = event.data.data
}else if (event.data.type === "weilin_prompt_ui_get_template_"+promptBoxRandomID) {
window.parent.postMessage({ type: 'weilin_prompt_ui_get_template_response', id: promptBoxRandomID, data: nodeTextAreaList[4].value }, '*')
}else if (event.data.type === "weilin_prompt_ui_get_template_go_random_"+promptBoxRandomID) {
window.parent.postMessage({ type: 'weilin_prompt_ui_get_template_go_random_response', id: promptBoxRandomID, data: nodeTextAreaList[4].value }, '*')
}
};
// 注册消息监听器
window.addEventListener('message', messageHandler, false);
// 添加按钮点击事件
if (nodeData.name === "WeiLinPromptUI" || nodeData.name === "WeiLinPromptUIWithoutLora") {
// 节点按钮点击事件 - 打开提示词编辑器
let promptOpenWidget = null;
promptOpenWidget = this.addWidget("button", localLanguage, '', async ($e) => {
// 先加载资源(如果还未加载)
await loadResourcesOnDemand();
// console.log(thisNodeName)
// 发送消息给父窗口
// console.log(global_randomID)
promptBoxRandomID = generateUUID();
// console.log("register====>",promptBoxRandomID)
let jsonData = {
prompt: promptOpenWidget._node.widgets[0].value,
lora: [],
temp_prompt: {},
temp_lora: {},
}
if (nodeData.name === "WeiLinPromptUI" && nodeTextAreaList[1] && nodeTextAreaList[1].value && nodeTextAreaList[1].value.length > 0) {
jsonData.lora = JSON.parse(nodeTextAreaList[1].value);
}
if (nodeTextAreaList[2] && nodeTextAreaList[2].value && nodeTextAreaList[2].value.length > 0) {
jsonData.temp_prompt = JSON.parse(nodeTextAreaList[2].value)
}
if (nodeData.name === "WeiLinPromptUI" && nodeTextAreaList[3] && nodeTextAreaList[3].value && nodeTextAreaList[3].value.length > 0) {
jsonData.temp_lora = JSON.parse(nodeTextAreaList[3].value)
}
const data = JSON.stringify(jsonData)
window.parent.postMessage({ type: 'weilin_prompt_ui_openPromptBox', id: promptBoxRandomID, prompt: data, node: nodeData.name }, '*')
});
}
if (nodeData.name === "WeiLinPromptUI" || nodeData.name === "WeiLinPromptUIOnlyLoraStack") {
// 节点按钮点击事件 - 打开Lora堆
this.addWidget("button", localOpenLoraLanguage, '', async ($e) => {
// 先加载资源(如果还未加载)
await loadResourcesOnDemand();
// 使用 thisNodeSeed 而不是生成新的UUID,确保数据能正确同步
// console.log(thisNodeName)
// 发送消息给父窗口
// console.log(global_randomID)
let jsonData = {
lora: [],
temp_lora: {},
}
if (nodeTextAreaList[1] && nodeTextAreaList[1].value && nodeTextAreaList[1].value.length > 0) {
jsonData.lora = JSON.parse(nodeTextAreaList[1].value);
}
if (nodeTextAreaList[3] && nodeTextAreaList[3].value && nodeTextAreaList[3].value.length > 0) {
jsonData.temp_lora = JSON.parse(nodeTextAreaList[3].value)
}
const data = JSON.stringify(jsonData)
// 区分两种节点类型,使用不同的消息类型
// WeiLinPromptUI: 使用独立的LoRA堆窗口,与内嵌LoRA堆同步
// WeiLinPromptUIOnlyLoraStack: 使用独立的LoRA堆窗口,独立管理
const messageType = nodeData.name === "WeiLinPromptUI"
? 'weilin_prompt_ui_open_prompt_lora_stack_window'
: 'weilin_prompt_ui_open_node_lora_stack_window';
window.parent.postMessage({ type: messageType, seed: thisNodeSeed, prompt: data, node: nodeData.name }, '*')
});
}
// 保存原有的onRemoved函数
const originalOnRemoved = this.onRemoved;
// 节点被删除事件
this.onRemoved = () => {
console.log('[WeiLin] onRemoved called for:', nodeData.name);
// 调用原有的onRemoved函数
if (originalOnRemoved) {
originalOnRemoved.apply(this);
}
// 移除消息监听器,防止内存泄漏和事件冲突
window.removeEventListener('message', messageHandler, false);
// 清理可能残留的遮罩层
const overlays = document.querySelectorAll('.loading-overlay, .weilin-comfyui-loading-overlay');
overlays.forEach(overlay => {
overlay.remove();
});
// 元素被销毁 事件发送更新元素
if (nodeData.name === "WeiLinPromptUI" ||
nodeData.name === "WeiLinPromptUIWithoutLora") {
removeNodeBySeed(thisNodeSeed);
window.parent.postMessage({ type: 'weilin_prompt_ui_update_node_list_info', nodeList: globalNodeList }, '*')
}
// 清理Lora Stack相关数据
if (nodeData.name === "WeiLinPromptUIOnlyLoraStack") {
if (window.weilinGlobalSelectedLoras && window.weilinGlobalSelectedLoras[thisNodeSeed]) {
delete window.weilinGlobalSelectedLoras[thisNodeSeed];
}
}
}
return r;
};
// When the node is executed we will be sent the input text, display this in the widget
const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function (message) {
onExecuted?.apply(this, arguments);
const positiveWidget = this.widgets.find(w => w.name === "positive");
if (positiveWidget && message.positive) {
positiveWidget.element.value = message.positive;
// 触发input事件以更新全局状态
const event = new Event('input', { bubbles: true });
positiveWidget.element.dispatchEvent(event);
}
// console.log(message.positive)
};
}
},
});
}); // waitForApp回调结束
} // 防止重复注册的else块结束
//from melmass
// https://github.com/kijai/ComfyUI-KJNodes/blob/main/web/js/spline_editor.js
function hideWidgetForGood(node, widget, suffix = '') {
widget.origType = widget.type
widget.origComputeSize = widget.computeSize
widget.origSerializeValue = widget.serializeValue
widget.computeSize = () => [0, -4] // -4 is due to the gap litegraph adds between widgets automatically
widget.type = "converted-widget" + suffix
widget.element.setAttribute("id", "weilin-hidden-weight");
widget.element.style.display = 'none'
// 启用序列化,确保widget值被保存到工作流JSON中
widget.serializeValue = () => {
return widget.value;
}
// Hide any linked widgets, e.g. seed+seedControl
if (widget.linkedWidgets) {
for (const w of widget.linkedWidgets) {
hideWidgetForGood(node, w, ':' + widget.name)
}
}
}
function createLoraStackWidget(node, seed, ptEl) {
var element = document.createElement("div");
const previewNode = node;
const prSeed = seed;
const prTempLoraEl = ptEl;
var previewWidget = node.addDOMWidget("weilin_lora_stack", "lora_stack", element, {
serialize: false,
hideOnZoom: false,
getValue() {
return element.value;
},
setValue(v) {
element.value = v;
},
});
previewNode.onResize = function () {
let [w, h] = previewNode.size;
if (h < 300) h = 300;
previewNode.size = [w, h];
};
previewWidget.value = { hidden: false, paused: false, params: {} }
previewWidget.parentEl = document.createElement("div");
previewWidget.parentEl.className = "weilin-comfyui-lora-stack";
element.appendChild(previewWidget.parentEl);
const lang = navigator.language || navigator.userLanguage;
const localLang = lang.startsWith('zh') ? 'zh' : 'en';
previewWidget.contentEl = document.createElement("div");
previewWidget.contentEl.innerHTML = `
<div class="weilin-comfyui-lora-header">
<div class="weilin-comfyui-header-actions">
<button class="weilin-comfyui-add-btn" id="addLoraBtn_`+prSeed+`" data-seed="`+prSeed+`" title="${localLang === 'zh' ? '添加Lora' : 'Add Lora' }">
<svg viewBox="0 0 24 24" width="16" height="16">
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
</svg>
</button>
</div>
</div>
<div class="weilin-comfyui-lora-body">
<div class="weilin-comfyui-lora-list" id="loraListContainer_`+prSeed+`">
<!-- Lora items will be added here dynamically -->
</div>
</div>
`
previewWidget.contentEl.className = "weilin-comfyui-lora-content"
previewWidget.parentEl.appendChild(previewWidget.contentEl)
// 使用 querySelector 在当前元素内查找按钮,而不是 document.getElementById
const addLoraBtn = previewWidget.contentEl.querySelector('#addLoraBtn_' + prSeed);
if (addLoraBtn) {
addLoraBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const seed = this.getAttribute('data-seed');
console.log('[WeiLin] Add Lora button clicked, seed:', seed);
if (seed && typeof openLoraManager === 'function') {
openLoraManager(this);
} else {
console.warn('[WeiLin] openLoraManager function not found or seed is invalid');
}
});
// 阻止事件冒泡,防止被 LiteGraph 拦截
addLoraBtn.addEventListener('mousedown', function(e) {
e.stopPropagation();
});
addLoraBtn.addEventListener('mouseup', function(e) {
e.stopPropagation();
});
} else {
console.warn('[WeiLin] Add Lora button not found for seed:', prSeed);
}
setTimeout(() => {
if (prTempLoraEl.value.length > 0) {
window.weilinGlobalSelectedLoras[seed] = JSON.parse(prTempLoraEl.value)
}else {
window.weilinGlobalSelectedLoras[seed]= []
}
renderAllLoras(seed)
// console.log(window.weilinGlobalSelectedLoras)
},300)
// console.log(node)
}