mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
Enhance lora loader done
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
import re
|
|
||||||
from nodes import LoraLoader
|
from nodes import LoraLoader
|
||||||
from comfy.comfy_types import IO # type: ignore
|
from comfy.comfy_types import IO # type: ignore
|
||||||
from ..services.lora_scanner import LoraScanner
|
from ..services.lora_scanner import LoraScanner
|
||||||
from ..config import config
|
from ..config import config
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
|
from .utils import FlexibleOptionalInputType, any_type
|
||||||
|
|
||||||
class LoraManagerLoader:
|
class LoraManagerLoader:
|
||||||
NAME = "Lora Loader (LoraManager)"
|
NAME = "Lora Loader (LoraManager)"
|
||||||
@@ -23,10 +23,11 @@ class LoraManagerLoader:
|
|||||||
"placeholder": "LoRA syntax input: <lora:name:strength>"
|
"placeholder": "LoRA syntax input: <lora:name:strength>"
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
"optional": FlexibleOptionalInputType(any_type),
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TYPES = ("MODEL", "CLIP", IO.STRING, IO.STRING)
|
RETURN_TYPES = ("MODEL", "CLIP", IO.STRING)
|
||||||
RETURN_NAMES = ("MODEL", "CLIP", "loaded_loras", "trigger_words")
|
RETURN_NAMES = ("MODEL", "CLIP", "trigger_words")
|
||||||
FUNCTION = "load_loras"
|
FUNCTION = "load_loras"
|
||||||
|
|
||||||
async def get_lora_info(self, lora_name):
|
async def get_lora_info(self, lora_name):
|
||||||
@@ -49,31 +50,28 @@ class LoraManagerLoader:
|
|||||||
return lora_name, [] # Fallback if not found
|
return lora_name, [] # Fallback if not found
|
||||||
|
|
||||||
def load_loras(self, model, clip, text, **kwargs):
|
def load_loras(self, model, clip, text, **kwargs):
|
||||||
"""Loads multiple LoRAs based on the text input format."""
|
"""Loads multiple LoRAs based on the kwargs input."""
|
||||||
for key, value in kwargs.items():
|
|
||||||
print(f"{key}: {value}")
|
|
||||||
|
|
||||||
lora_pattern = r'<lora:([^:]+):([\d\.]+)>'
|
|
||||||
lora_matches = re.finditer(lora_pattern, text)
|
|
||||||
|
|
||||||
loaded_loras = []
|
loaded_loras = []
|
||||||
all_trigger_words = []
|
all_trigger_words = []
|
||||||
|
|
||||||
for match in lora_matches:
|
if 'loras' in kwargs:
|
||||||
lora_name = match.group(1)
|
for lora in kwargs['loras']:
|
||||||
strength = float(match.group(2))
|
if not lora.get('active', False):
|
||||||
|
continue
|
||||||
# Get lora path and trigger words
|
|
||||||
lora_path, trigger_words = asyncio.run(self.get_lora_info(lora_name))
|
lora_name = lora['name']
|
||||||
|
strength = float(lora['strength'])
|
||||||
# Apply the LoRA using the resolved path
|
|
||||||
model, clip = LoraLoader().load_lora(model, clip, lora_path, strength, strength)
|
# Get lora path and trigger words
|
||||||
loaded_loras.append(f"{lora_name}: {strength}")
|
lora_path, trigger_words = asyncio.run(self.get_lora_info(lora_name))
|
||||||
|
|
||||||
# Add trigger words to collection
|
# Apply the LoRA using the resolved path
|
||||||
all_trigger_words.extend(trigger_words)
|
model, clip = LoraLoader().load_lora(model, clip, lora_path, strength, strength)
|
||||||
|
loaded_loras.append(f"{lora_name}: {strength}")
|
||||||
|
|
||||||
|
# Add trigger words to collection
|
||||||
|
all_trigger_words.extend(trigger_words)
|
||||||
|
|
||||||
loaded_loras_text = "\n".join(loaded_loras) if loaded_loras else "No LoRAs loaded"
|
|
||||||
trigger_words_text = ", ".join(all_trigger_words) if all_trigger_words else ""
|
trigger_words_text = ", ".join(all_trigger_words) if all_trigger_words else ""
|
||||||
|
|
||||||
return (model, clip, loaded_loras_text, trigger_words_text)
|
return (model, clip, trigger_words_text)
|
||||||
@@ -41,6 +41,7 @@ class ApiRoutes:
|
|||||||
app.router.add_post('/api/settings', routes.update_settings)
|
app.router.add_post('/api/settings', routes.update_settings)
|
||||||
app.router.add_post('/api/move_model', routes.move_model)
|
app.router.add_post('/api/move_model', routes.move_model)
|
||||||
app.router.add_post('/loras/api/save-metadata', routes.save_metadata)
|
app.router.add_post('/loras/api/save-metadata', routes.save_metadata)
|
||||||
|
app.router.add_get('/api/lora-preview-url', routes.get_lora_preview_url) # Add new route
|
||||||
|
|
||||||
async def delete_model(self, request: web.Request) -> web.Response:
|
async def delete_model(self, request: web.Request) -> web.Response:
|
||||||
"""Handle model deletion request"""
|
"""Handle model deletion request"""
|
||||||
@@ -593,3 +594,38 @@ class ApiRoutes:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error saving metadata: {e}", exc_info=True)
|
logger.error(f"Error saving metadata: {e}", exc_info=True)
|
||||||
return web.Response(text=str(e), status=500)
|
return web.Response(text=str(e), status=500)
|
||||||
|
|
||||||
|
async def get_lora_preview_url(self, request: web.Request) -> web.Response:
|
||||||
|
"""Get the static preview URL for a LoRA file"""
|
||||||
|
try:
|
||||||
|
# Get lora file name from query parameters
|
||||||
|
lora_name = request.query.get('name')
|
||||||
|
if not lora_name:
|
||||||
|
return web.Response(text='Lora file name is required', status=400)
|
||||||
|
|
||||||
|
# Get cache data
|
||||||
|
cache = await self.scanner.get_cached_data()
|
||||||
|
|
||||||
|
# Search for the lora in cache data
|
||||||
|
for lora in cache.raw_data:
|
||||||
|
file_name = lora['file_name']
|
||||||
|
if file_name == lora_name:
|
||||||
|
if preview_url := lora.get('preview_url'):
|
||||||
|
# Convert preview path to static URL
|
||||||
|
static_url = config.get_preview_static_url(preview_url)
|
||||||
|
if static_url:
|
||||||
|
return web.json_response({
|
||||||
|
'success': True,
|
||||||
|
'preview_url': static_url
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
|
# If no preview URL found
|
||||||
|
return web.json_response({
|
||||||
|
'success': False,
|
||||||
|
'error': 'No preview URL found for the specified lora'
|
||||||
|
}, status=404)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting lora preview URL: {e}", exc_info=True)
|
||||||
|
return web.Response(text=str(e), status=500)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
import { addLorasWidget } from "./loras_widget.js";
|
import { addLorasWidget } from "./loras_widget.js";
|
||||||
import { hideWidgetForGood } from "./utils.js";
|
|
||||||
|
|
||||||
function mergeLoras(lorasText, lorasJson) {
|
function mergeLoras(lorasText, lorasJson) {
|
||||||
const result = [];
|
const result = [];
|
||||||
@@ -29,10 +28,6 @@ function mergeLoras(lorasText, lorasJson) {
|
|||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "LoraManager.LoraLoader",
|
name: "LoraManager.LoraLoader",
|
||||||
|
|
||||||
setup(...args) {
|
|
||||||
console.log("LoraLoader setup args:", args);
|
|
||||||
},
|
|
||||||
|
|
||||||
async nodeCreated(node) {
|
async nodeCreated(node) {
|
||||||
if (node.comfyClass === "Lora Loader (LoraManager)") {
|
if (node.comfyClass === "Lora Loader (LoraManager)") {
|
||||||
// Enable widget serialization
|
// Enable widget serialization
|
||||||
@@ -43,9 +38,14 @@ app.registerExtension({
|
|||||||
// Restore saved value if exists
|
// Restore saved value if exists
|
||||||
let existingLoras = [];
|
let existingLoras = [];
|
||||||
if (node.widgets_values && node.widgets_values.length > 0) {
|
if (node.widgets_values && node.widgets_values.length > 0) {
|
||||||
// 0 is input, 1 is loras widget
|
const savedValue = node.widgets_values[1];
|
||||||
try {
|
try {
|
||||||
existingLoras = JSON.parse(node.widgets_values[1]);
|
// Check if the value is already an array/object
|
||||||
|
if (typeof savedValue === 'object' && savedValue !== null) {
|
||||||
|
existingLoras = savedValue;
|
||||||
|
} else if (typeof savedValue === 'string') {
|
||||||
|
existingLoras = JSON.parse(savedValue);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Failed to parse loras data:", e);
|
console.warn("Failed to parse loras data:", e);
|
||||||
existingLoras = [];
|
existingLoras = [];
|
||||||
@@ -72,8 +72,6 @@ app.registerExtension({
|
|||||||
|
|
||||||
node.lorasWidget.value = mergedLoras;
|
node.lorasWidget.value = mergedLoras;
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("node: ", node);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { api } from "../../scripts/api.js";
|
||||||
|
|
||||||
export function addLorasWidget(node, name, opts, callback) {
|
export function addLorasWidget(node, name, opts, callback) {
|
||||||
// Create container for loras
|
// Create container for loras
|
||||||
const container = document.createElement("div");
|
const container = document.createElement("div");
|
||||||
@@ -104,6 +106,129 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
return button;
|
return button;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 添加预览弹窗组件
|
||||||
|
class PreviewTooltip {
|
||||||
|
constructor() {
|
||||||
|
this.element = document.createElement('div');
|
||||||
|
Object.assign(this.element.style, {
|
||||||
|
position: 'fixed',
|
||||||
|
zIndex: 9999,
|
||||||
|
padding: '4px', // 减小内边距
|
||||||
|
background: 'rgba(0, 0, 0, 0.75)', // 稍微调整透明度
|
||||||
|
borderRadius: '4px', // 减小圆角
|
||||||
|
boxShadow: '0 2px 6px rgba(0, 0, 0, 0.15)', // 减小阴影
|
||||||
|
display: 'none',
|
||||||
|
maxWidth: '300px',
|
||||||
|
maxHeight: '300px',
|
||||||
|
});
|
||||||
|
document.body.appendChild(this.element);
|
||||||
|
this.hideTimeout = null; // 添加超时处理变量
|
||||||
|
}
|
||||||
|
|
||||||
|
async show(loraName, x, y) {
|
||||||
|
try {
|
||||||
|
// 清除之前的隐藏定时器
|
||||||
|
if (this.hideTimeout) {
|
||||||
|
clearTimeout(this.hideTimeout);
|
||||||
|
this.hideTimeout = null;
|
||||||
|
}
|
||||||
|
// 获取预览URL
|
||||||
|
const response = await api.fetchApi(`/lora-preview-url?name=${encodeURIComponent(loraName)}`, {
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch preview URL');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (!data.success || !data.preview_url) {
|
||||||
|
throw new Error('No preview available');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除现有内容
|
||||||
|
while (this.element.firstChild) {
|
||||||
|
this.element.removeChild(this.element.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为视频
|
||||||
|
const isVideo = data.preview_url.endsWith('.mp4');
|
||||||
|
const mediaElement = isVideo ?
|
||||||
|
document.createElement('video') :
|
||||||
|
document.createElement('img');
|
||||||
|
|
||||||
|
Object.assign(mediaElement.style, {
|
||||||
|
maxWidth: '300px',
|
||||||
|
maxHeight: '300px',
|
||||||
|
objectFit: 'contain'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isVideo) {
|
||||||
|
mediaElement.autoplay = true;
|
||||||
|
mediaElement.loop = true;
|
||||||
|
mediaElement.muted = true;
|
||||||
|
mediaElement.controls = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaElement.src = data.preview_url;
|
||||||
|
|
||||||
|
this.element.appendChild(mediaElement);
|
||||||
|
this.position(x, y);
|
||||||
|
this.element.style.display = 'block';
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to load preview:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
position(x, y) {
|
||||||
|
// 确保预览框不超出视窗边界
|
||||||
|
const rect = this.element.getBoundingClientRect();
|
||||||
|
const viewportWidth = window.innerWidth;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
|
||||||
|
let left = x + 10; // 默认在鼠标右侧偏移10px
|
||||||
|
let top = y + 10; // 默认在鼠标下方偏移10px
|
||||||
|
|
||||||
|
// 检查右边界
|
||||||
|
if (left + rect.width > viewportWidth) {
|
||||||
|
left = x - rect.width - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查下边界
|
||||||
|
if (top + rect.height > viewportHeight) {
|
||||||
|
top = y - rect.height - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(this.element.style, {
|
||||||
|
left: `${left}px`,
|
||||||
|
top: `${top}px`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
// 使用延迟来确保隐藏事件在显示事件之后执行
|
||||||
|
this.hideTimeout = setTimeout(() => {
|
||||||
|
this.element.style.display = 'none';
|
||||||
|
// 停止视频播放
|
||||||
|
const video = this.element.querySelector('video');
|
||||||
|
if (video) {
|
||||||
|
video.pause();
|
||||||
|
}
|
||||||
|
this.hideTimeout = null;
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if (this.hideTimeout) {
|
||||||
|
clearTimeout(this.hideTimeout);
|
||||||
|
}
|
||||||
|
this.element.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建预览tooltip实例
|
||||||
|
const previewTooltip = new PreviewTooltip();
|
||||||
|
|
||||||
// Function to render loras from data
|
// Function to render loras from data
|
||||||
const renderLoras = (value, widget) => {
|
const renderLoras = (value, widget) => {
|
||||||
// Clear existing content
|
// Clear existing content
|
||||||
@@ -234,8 +359,36 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
color: active ? "rgba(226, 232, 240, 0.9)" : "rgba(226, 232, 240, 0.6)",
|
color: active ? "rgba(226, 232, 240, 0.9)" : "rgba(226, 232, 240, 0.6)",
|
||||||
fontSize: "13px",
|
fontSize: "13px",
|
||||||
|
cursor: "pointer", // Add pointer cursor to indicate hoverable area
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Move preview tooltip events to nameEl instead of loraEl
|
||||||
|
nameEl.addEventListener('mouseenter', async (e) => {
|
||||||
|
e.stopPropagation(); // 阻止事件冒泡
|
||||||
|
await previewTooltip.show(name, e.clientX, e.clientY);
|
||||||
|
});
|
||||||
|
|
||||||
|
nameEl.addEventListener('mousemove', (e) => {
|
||||||
|
e.stopPropagation(); // 阻止事件冒泡
|
||||||
|
if (previewTooltip.element.style.display === 'block') {
|
||||||
|
previewTooltip.position(e.clientX, e.clientY);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
nameEl.addEventListener('mouseleave', (e) => {
|
||||||
|
e.stopPropagation(); // 阻止事件冒泡
|
||||||
|
previewTooltip.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove the preview tooltip events from loraEl
|
||||||
|
loraEl.onmouseenter = () => {
|
||||||
|
loraEl.style.backgroundColor = active ? "rgba(50, 60, 80, 0.8)" : "rgba(40, 45, 55, 0.6)";
|
||||||
|
};
|
||||||
|
|
||||||
|
loraEl.onmouseleave = () => {
|
||||||
|
loraEl.style.backgroundColor = active ? "rgba(45, 55, 72, 0.7)" : "rgba(35, 40, 50, 0.5)";
|
||||||
|
};
|
||||||
|
|
||||||
// Create strength control
|
// Create strength control
|
||||||
const strengthControl = document.createElement("div");
|
const strengthControl = document.createElement("div");
|
||||||
Object.assign(strengthControl.style, {
|
Object.assign(strengthControl.style, {
|
||||||
@@ -264,14 +417,77 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Strength display
|
// Strength display
|
||||||
const strengthEl = document.createElement("div");
|
const strengthEl = document.createElement("input");
|
||||||
const displayStrength = typeof strength === 'number' ? strength.toFixed(2) : Number(strength).toFixed(2);
|
strengthEl.type = "text";
|
||||||
strengthEl.textContent = displayStrength;
|
strengthEl.value = typeof strength === 'number' ? strength.toFixed(2) : Number(strength).toFixed(2);
|
||||||
Object.assign(strengthEl.style, {
|
Object.assign(strengthEl.style, {
|
||||||
minWidth: "36px",
|
minWidth: "50px",
|
||||||
|
width: "50px",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
color: active ? "rgba(226, 232, 240, 0.9)" : "rgba(226, 232, 240, 0.6)",
|
color: active ? "rgba(226, 232, 240, 0.9)" : "rgba(226, 232, 240, 0.6)",
|
||||||
fontSize: "13px",
|
fontSize: "13px",
|
||||||
|
background: "none",
|
||||||
|
border: "1px solid transparent",
|
||||||
|
padding: "2px 4px",
|
||||||
|
borderRadius: "3px",
|
||||||
|
outline: "none",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加hover效果
|
||||||
|
strengthEl.addEventListener('mouseenter', () => {
|
||||||
|
strengthEl.style.border = "1px solid rgba(226, 232, 240, 0.2)";
|
||||||
|
});
|
||||||
|
|
||||||
|
strengthEl.addEventListener('mouseleave', () => {
|
||||||
|
if (document.activeElement !== strengthEl) {
|
||||||
|
strengthEl.style.border = "1px solid transparent";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理焦点
|
||||||
|
strengthEl.addEventListener('focus', () => {
|
||||||
|
strengthEl.style.border = "1px solid rgba(66, 153, 225, 0.6)";
|
||||||
|
strengthEl.style.background = "rgba(0, 0, 0, 0.2)";
|
||||||
|
// 自动选中所有内容
|
||||||
|
strengthEl.select();
|
||||||
|
});
|
||||||
|
|
||||||
|
strengthEl.addEventListener('blur', () => {
|
||||||
|
strengthEl.style.border = "1px solid transparent";
|
||||||
|
strengthEl.style.background = "none";
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理输入变化
|
||||||
|
strengthEl.addEventListener('change', () => {
|
||||||
|
let newValue = parseFloat(strengthEl.value);
|
||||||
|
|
||||||
|
// 验证输入
|
||||||
|
if (isNaN(newValue)) {
|
||||||
|
newValue = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新数值
|
||||||
|
const lorasData = parseLoraValue(widget.value);
|
||||||
|
const loraIndex = lorasData.findIndex(l => l.name === name);
|
||||||
|
|
||||||
|
if (loraIndex >= 0) {
|
||||||
|
lorasData[loraIndex].strength = newValue.toFixed(2);
|
||||||
|
|
||||||
|
// 更新值并触发回调
|
||||||
|
const newLorasValue = formatLoraValue(lorasData);
|
||||||
|
widget.value = newLorasValue;
|
||||||
|
widget.callback?.(newLorasValue);
|
||||||
|
|
||||||
|
// 重新渲染
|
||||||
|
renderLoras(newLorasValue, widget);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理按键事件
|
||||||
|
strengthEl.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
strengthEl.blur();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Right arrow
|
// Right arrow
|
||||||
@@ -312,15 +528,6 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
loraEl.appendChild(leftSection);
|
loraEl.appendChild(leftSection);
|
||||||
loraEl.appendChild(strengthControl);
|
loraEl.appendChild(strengthControl);
|
||||||
|
|
||||||
// Add hover effect to the lora entry
|
|
||||||
loraEl.onmouseenter = () => {
|
|
||||||
loraEl.style.backgroundColor = active ? "rgba(50, 60, 80, 0.8)" : "rgba(40, 45, 55, 0.6)";
|
|
||||||
};
|
|
||||||
|
|
||||||
loraEl.onmouseleave = () => {
|
|
||||||
loraEl.style.backgroundColor = active ? "rgba(45, 55, 72, 0.7)" : "rgba(35, 40, 50, 0.5)";
|
|
||||||
};
|
|
||||||
|
|
||||||
container.appendChild(loraEl);
|
container.appendChild(loraEl);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -345,9 +552,6 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
lorasCount > 0 ? 60 + lorasCount * 44 : 60 // Header + entries or minimum height
|
lorasCount > 0 ? 60 + lorasCount * 44 : 60 // Header + entries or minimum height
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onDraw: function() {
|
|
||||||
// Empty function
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize widget value using options methods
|
// Initialize widget value using options methods
|
||||||
@@ -355,11 +559,17 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
|
|
||||||
widget.callback = callback;
|
widget.callback = callback;
|
||||||
|
|
||||||
|
widget.computeSize = (width, height) => {
|
||||||
|
// console.log("loras_widget computeSize called: ", width, height);
|
||||||
|
return [400, 200];
|
||||||
|
};
|
||||||
|
|
||||||
// Render initial state
|
// Render initial state
|
||||||
renderLoras(widgetValue, widget);
|
renderLoras(widgetValue, widget);
|
||||||
|
|
||||||
widget.onRemove = () => {
|
widget.onRemove = () => {
|
||||||
container.remove();
|
container.remove();
|
||||||
|
previewTooltip.cleanup();
|
||||||
};
|
};
|
||||||
|
|
||||||
return { minWidth: 400, minHeight: 200, widget };
|
return { minWidth: 400, minHeight: 200, widget };
|
||||||
|
|||||||
Reference in New Issue
Block a user