fix:latentNoisy & preSamplingNoiseIn & Unsampler

This commit is contained in:
yolain
2024-04-07 12:40:24 +08:00
parent 96125a65a0
commit 35f19b75fa
15 changed files with 593 additions and 201 deletions
+14 -2
View File
@@ -31,6 +31,12 @@
## Changelog
**v1.1.4**
- Added the right-click menu to view checkpoints and lora information in all Loaders
- Fixed `easy preSamplingNoiseIn`、`easy latentNoisy`、`east Unsampler` compatible with ComfyUI Revision>=2098 [0542088e] or later
**v1.1.3 (2024/4/4)**
- `easy ipadapterApply` Added **COMPOSITION** preset
@@ -38,7 +44,7 @@
- Added `easy promptLine`
- Added `easy promptReplace`
- Added `easy promptConcat`
- `easy wildcards` 增加 **multiline_mode**属性
- `easy wildcards` Added **multiline_mode**
**v1.1.2 (39c5ccf)**
@@ -320,4 +326,10 @@ Disclaimer: Opened source was not easy. I have a lot of respect for the contribu
[ComfyUI-Inspire-Pack](https://github.com/ltdrdata/ComfyUI-Inspire-Pack) - General Modpack 2
[ComfyUI-ResAdapter](https://github.com/jiaxiangc/ComfyUI-ResAdapter) - Make model generation independent of training resolution
[ComfyUI-ResAdapter](https://github.com/jiaxiangc/ComfyUI-ResAdapter) - Make model generation independent of training resolution
[ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) - Style migration
[ComfyUI_InstantID](https://github.com/cubiq/ComfyUI_InstantID) - Face migration
[ComfyUI-Custom-Scripts](https://github.com/pythongosssss/ComfyUI-Custom-Scripts) - pyssss🐍
+11
View File
@@ -35,6 +35,11 @@
## 更新日志
**v1.1.4 (2024/4/7)**
- 增加 在Loaders上右键菜单可查看 checkpoints、lora 信息
- 修复 `easy preSamplingNoiseIn`、`easy latentNoisy`、`east Unsampler` 以兼容ComfyUI Revision>=2098 [0542088e] 以上版本
**v1.1.3 (2024/4/4)**
- `easy ipadapterApply` 增加 **COMPOSITION** 预置项
@@ -338,3 +343,9 @@
[ComfyUI-Logic](https://github.com/theUpsider/ComfyUI-Logic) - ComfyUI逻辑运算
[ComfyUI-ResAdapter](https://github.com/jiaxiangc/ComfyUI-ResAdapter) - 让模型生成不受训练分辨率限制
[ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) - 风格迁移
[ComfyUI_InstantID](https://github.com/cubiq/ComfyUI_InstantID) - 人脸迁移
[ComfyUI-Custom-Scripts](https://github.com/pythongosssss/ComfyUI-Custom-Scripts) - pyssss 小蛇🐍脚本
+35
View File
@@ -130,6 +130,41 @@ async def getModelsThumbnail(request):
loras_full.append(full_path)
return web.json_response(checkpoints_full + loras_full)
@PromptServer.instance.routes.post("/easyuse/metadata/notes/{name}")
async def save_notes(request):
name = request.match_info["name"]
pos = name.index("/")
type = name[0:pos]
name = name[pos+1:]
file_path = None
if type == "embeddings" or type == "loras":
name = name.lower()
files = folder_paths.get_filename_list(type)
for f in files:
lower_f = f.lower()
if lower_f == name:
file_path = folder_paths.get_full_path(type, f)
else:
n = os.path.splitext(f)[0].lower()
if n == name:
file_path = folder_paths.get_full_path(type, f)
if file_path is not None:
break
else:
file_path = folder_paths.get_full_path(
type, name)
if not file_path:
return web.Response(status=404)
file_no_ext = os.path.splitext(file_path)[0]
info_file = file_no_ext + ".txt"
with open(info_file, "w") as f:
f.write(await request.text())
return web.Response(status=200)
@PromptServer.instance.routes.get("/easyuse/metadata/{name}")
async def load_metadata(request):
name = request.match_info["name"]
+13 -13
View File
@@ -1,7 +1,7 @@
import sys, os, re, json, time, math
import torch
import folder_paths
import comfy.utils, comfy.samplers, comfy.controlnet, comfy.model_base, comfy.model_management
import comfy.utils, comfy.sample, comfy.sampler_helpers, comfy.samplers, comfy.controlnet, comfy.model_base, comfy.model_management
from comfy.sd import CLIP, VAE
from comfy.model_patcher import ModelPatcher
from comfy_extras.chainner_models import model_loading
@@ -594,10 +594,9 @@ class latentNoisy:
device = comfy.model_management.get_torch_device()
end_at_step = min(steps, end_at_step)
start_at_step = min(start_at_step, end_at_step)
real_model = None
comfy.model_management.load_model_gpu(model)
real_model = model.model
sampler = comfy.samplers.KSampler(real_model, steps=steps, device=device, sampler=sampler_name,
model_patcher = comfy.model_patcher.ModelPatcher(model.model, load_device=device, offload_device=comfy.model_management.unet_offload_device())
sampler = comfy.samplers.KSampler(model_patcher, steps=steps, device=device, sampler=sampler_name,
scheduler=scheduler, denoise=1.0, model_options=model.model_options)
sigmas = sampler.sigmas
sigma = sigmas[start_at_step] - sigmas[end_at_step]
@@ -2903,8 +2902,8 @@ class samplerSettingsNoiseIn:
device = comfy.model_management.get_torch_device()
comfy.model_management.load_model_gpu(model)
real_model = model.model
sampler = comfy.samplers.KSampler(real_model, steps=steps, device=device, sampler=sampler_name,
model_patcher = comfy.model_patcher.ModelPatcher(model.model, load_device=device, offload_device=comfy.model_management.unet_offload_device())
sampler = comfy.samplers.KSampler(model_patcher, steps=steps, device=device, sampler=sampler_name,
scheduler=scheduler, denoise=1.0, model_options=model.model_options)
sigmas = sampler.sigmas
sigma = sigmas[start_at_step] - sigmas[end_at_step]
@@ -3483,6 +3482,7 @@ class samplerFull(LayerDiffuse):
"vae": ("VAE",),
"clip": ("CLIP",),
"xyPlot": ("XYPLOT",),
"image": ("IMAGE",),
},
"hidden":
{"tile_size": "INT", "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID",
@@ -4407,23 +4407,23 @@ class unsampler:
if "noise_mask" in latent:
noise_mask = comfy.sample.prepare_mask(latent["noise_mask"], noise.shape, device)
real_model = None
real_model = model.model
noise = noise.to(device)
latent_image = latent_image.to(device)
positive = comfy.sample.convert_cond(positive)
negative = comfy.sample.convert_cond(negative)
_positive = comfy.sampler_helpers.convert_cond(positive)
_negative = comfy.sampler_helpers.convert_cond(negative)
models, inference_memory = comfy.sampler_helpers.get_additional_models({"positive": _positive, "negative": _negative}, model.model_dtype())
models, inference_memory = comfy.sample.get_additional_models(positive, negative, model.model_dtype())
comfy.model_management.load_models_gpu([model] + models, model.memory_required(noise.shape) + inference_memory)
sampler = comfy.samplers.KSampler(real_model, steps=steps, device=device, sampler=sampler_name,
model_patcher = comfy.model_patcher.ModelPatcher(model.model, load_device=device, offload_device=comfy.model_management.unet_offload_device())
sampler = comfy.samplers.KSampler(model_patcher, steps=steps, device=device, sampler=sampler_name,
scheduler=scheduler, denoise=1.0, model_options=model.model_options)
sigmas = sigmas = sampler.sigmas.flip(0) + 0.0001
sigmas = sampler.sigmas.flip(0) + 0.0001
pbar = comfy.utils.ProgressBar(steps)
+12 -9
View File
@@ -125,6 +125,7 @@ use_mirror = False
def get_local_filepath(url, dirname, local_file_name=None):
"""Get local file path when is already downloaded or download it"""
import os
from server import PromptServer
from urllib.parse import urlparse
from torch.hub import download_url_to_file
global use_mirror
@@ -139,17 +140,19 @@ def get_local_filepath(url, dirname, local_file_name=None):
if use_mirror:
url = url.replace('huggingface.co', 'hf-mirror.com')
print(f'downloading {url} to {destination}')
PromptServer.instance.send_sync("easyuse-toast", {'content': f'Downloading model to {destination}, please wait...', 'duration': 10000})
download_url_to_file(url, destination)
except Exception as e:
args = e.args[0] if e.args is not None and len(e.args) > 0 else None
if isinstance(args, TimeoutError):
print(f'无法从 {url} 下载,正在尝试使用镜像地址...')
use_mirror = True
url = url.replace('huggingface.co', 'hf-mirror.com')
try:
download_url_to_file(url, destination)
except Exception as err:
raise Exception(f'无法从 {url} 下载,错误信息:{str(err.args[0])}')
use_mirror = True
url = url.replace('huggingface.co', 'hf-mirror.com')
print(f'无法从huggingface下载,正在尝试从 {url} 下载...')
PromptServer.instance.send_sync("easyuse-toast", {'content': f'无法连接huggingface,正在尝试从 {url} 下载...', 'duration': 10000})
try:
download_url_to_file(url, destination)
except Exception as err:
PromptServer.instance.send_sync("easyuse-toast",
{'content': f'无法从 {url} 下载模型'}, type='error')
raise Exception(f'无法从 {url} 下载,错误信息:{str(err.args[0])}')
return destination
def to_lora_patch_dict(state_dict: dict) -> dict:
+2 -1
View File
@@ -3,4 +3,5 @@
@import "selector.css";
@import "groupmap.css";
@import "contextmenu.css";
@import "modelinfo.css";
@import "modelinfo.css";
@import "toast.css";
+29 -3
View File
@@ -46,8 +46,9 @@
padding: 0;
}
.easyuse-model-tag {
background-color: rgb(128, 213, 247);
color: #000;
background-color: var(--comfy-input-bg);
border: 2px solid var(--border-color);
color: var(--input-text);
display: flex;
align-items: center;
gap: 5px;
@@ -66,7 +67,7 @@
text-align: center;
}
.easyuse-model-tag:hover {
outline: 2px solid var(--theme-color-light);
border: 2px solid var(--theme-color-light);
}
.easyuse-model-tag p {
margin: 0;
@@ -79,6 +80,7 @@
position: relative;
min-width: 20px;
overflow: hidden;
color: #fff;
}
.easyuse-model-metadata .comfy-modal-content {
@@ -229,6 +231,30 @@
padding: 5px 10px 5px 10px;
color: var(--input-text);
}
.easyuse-model-detail-textarea{
border-top:1px solid var(--border-color);
padding:10px;
height:100px;
overflow-y: auto;
font-size: 12px;
}
.easyuse-model-detail-textarea textarea{
width:100%;
height:100%;
border:0;
background-color:transparent;
color: var(--input-text);
}
.easyuse-model-detail-textarea textarea::placeholder{
color:var(--descrip-text);
}
.easyuse-model-detail-textarea.empty{
display: flex;
justify-content: center;
align-items: center;
color: var(--descrip-text);
}
.easyuse-model-notes {
background-color: rgba(0, 0, 0, 0.25);
padding: 5px;
+3
View File
@@ -1,5 +1,8 @@
:root {
--theme-color:#3f3eed;
--theme-color-light:#006691;
--success-color: #52c41a;
--error-color: #ff4d4f;
--warning-color: #faad14;
--font-family: Inter, -apple-system, BlinkMacSystemFont, Helvetica Neue, sans-serif;
}
+110
View File
@@ -0,0 +1,110 @@
.easyuse-toast-container{
position: fixed;
z-index: 99999;
top: 0;
left: 0;
width: 100%;
height: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: start;
padding:10px 0;
}
.easyuse-toast-container > div {
position: relative;
height: fit-content;
padding: 4px;
margin-top: -100px; /* re-set by JS */
opacity: 0;
transition: all 0.33s ease-in-out;
z-index: 3;
}
.easyuse-toast-container > div:last-child {
z-index: 2;
}
.easyuse-toast-container > div:not(.-show) {
z-index: 1;
}
.easyuse-toast-container > div.-show {
opacity: 1;
margin-top: 0px !important;
}
.easyuse-toast-container > div.-show {
opacity: 1;
transform: translateY(0%);
}
.easyuse-toast-container > div > div {
position: relative;
background: var(--comfy-menu-bg);
color: var(--input-text);
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: fit-content;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.88);
padding: 9px 12px;
border-radius: 8px;
font-family: Arial, sans-serif;
font-size: 14px;
pointer-events: all;
}
.easyuse-toast-container > div > div > span {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.easyuse-toast-container > div > div > span svg {
width: 16px;
height: auto;
margin-right: 8px;
}
.easyuse-toast-container > div > div > span svg[data-icon=info-circle]{
fill: var(--theme-color-light);
}
.easyuse-toast-container > div > div > span svg[data-icon=check-circle]{
fill: var(--success-color);
}
.easyuse-toast-container > div > div > span svg[data-icon=close-circle]{
fill: var(--error-color);
}
.easyuse-toast-container > div > div > span svg[data-icon=exclamation-circle]{
fill: var(--warning-color);
}
/*rotate animation*/
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.easyuse-toast-container > div > div > span svg[data-icon=loading]{
fill: var(--theme-color);
animation: rotate 1s linear infinite;
}
.easyuse-toast-container a {
cursor: pointer;
text-decoration: underline;
color: var(--theme-color-light);
margin-left: 4px;
display: inline-block;
line-height: 1;
}
.easyuse-toast-container a:hover {
color: var(--theme-color-light);
text-decoration: none;
}
+12
View File
@@ -9,9 +9,21 @@ const zhCN = {
// ModelInfo
"Updated At:": "最近更新:",
"Created At:": "首次发布:",
"✏️ Edit": "✏️ 编辑",
"💾 Save": "💾 保存",
"No notes": "当前还没有备注内容",
"Saving Notes...": "正在保存备注...",
"Type your notes here":"在这里输入备注内容",
"Notes": "备注",
"Type": "类型",
"Trained Words": "训练词",
"BaseModel": "基础算法",
"Details": "详情",
"Download": "下载量",
"Source": "来源",
"Saving Preview...": "正在保存预览图...",
"Saving Succeed":"保存成功",
"Saving Failed":"保存失败",
// GroupMap
"Groups Map (EasyUse)": "管理组 (EasyUse)",
"Always": "启用中",
+193 -155
View File
@@ -2,7 +2,7 @@ import { $el, ComfyDialog } from "/scripts/ui.js";
import { api } from "/scripts/api.js";
import {formatTime} from './utils.js';
import {$t} from "./i18n.js";
import {toast} from "./toast.js";
class MetadataDialog extends ComfyDialog {
constructor() {
@@ -93,115 +93,47 @@ export class ModelInfoDialog extends ComfyDialog {
return btns;
}
getNoteInfo() {
function parseNote() {
if (!this.customNotes) return [];
parseNote() {
if (!this.customNotes) return [];
let notes = [];
// Extract links from notes
const r = new RegExp("(\\bhttps?:\\/\\/[^\\s]+)", "g");
let end = 0;
let m;
do {
m = r.exec(this.customNotes);
let pos;
let fin = 0;
if (m) {
pos = m.index;
fin = m.index + m[0].length;
} else {
pos = this.customNotes.length;
}
let notes = [];
// Extract links from notes
const r = new RegExp("(\\bhttps?:\\/\\/[^\\s]+)", "g");
let end = 0;
let m;
do {
m = r.exec(this.customNotes);
let pos;
let fin = 0;
if (m) {
pos = m.index;
fin = m.index + m[0].length;
} else {
pos = this.customNotes.length;
}
let pre = this.customNotes.substring(end, pos);
if (pre) {
pre = pre.replaceAll("\n", "<br>");
notes.push(
$el("span", {
innerHTML: pre,
})
);
}
if (m) {
notes.push(
$el("a", {
href: m[0],
textContent: m[0],
target: "_blank",
})
);
}
let pre = this.customNotes.substring(end, pos);
if (pre) {
pre = pre.replaceAll("\n", "<br>");
notes.push(
$el("span", {
innerHTML: pre,
})
);
}
if (m) {
notes.push(
$el("a", {
href: m[0],
textContent: m[0],
target: "_blank",
})
);
}
end = fin;
} while (m);
return notes;
}
let textarea;
let notesContainer;
const editText = "✏️ Edit";
const edit = $el("a", {
textContent: editText,
href: "#",
style: {
float: "right",
color: "greenyellow",
textDecoration: "none",
},
onclick: async (e) => {
e.preventDefault();
if (textarea) {
this.customNotes = textarea.value;
const resp = await api.fetchApi(
"/pysssss/metadata/notes/" + encodeURIComponent(`${this.type}/${this.name}`),
{
method: "POST",
body: this.customNotes,
}
);
if (resp.status !== 200) {
console.error(resp);
alert(`Error saving notes (${req.status}) ${req.statusText}`);
return;
}
e.target.textContent = editText;
textarea.remove();
textarea = null;
notesContainer.replaceChildren(...parseNote.call(this));
} else {
e.target.textContent = "💾 Save";
textarea = $el("textarea", {
style: {
width: "100%",
minWidth: "200px",
minHeight: "50px",
},
textContent: this.customNotes,
});
e.target.after(textarea);
notesContainer.replaceChildren();
textarea.style.height = Math.min(textarea.scrollHeight, 300) + "px";
}
},
});
notesContainer = $el("div.easyuse-model-notes", parseNote.call(this));
return $el(
"div",
{
style: { display: "contents" },
},
[edit, notesContainer]
);
}
addInfo() {
// this.addInfoEntry("Notes", this.getNoteInfo());
end = fin;
} while (m);
return notes;
}
addInfoEntry(name, value) {
@@ -250,6 +182,7 @@ export class ModelInfoDialog extends ComfyDialog {
return promise
.then((info) => {
this.imgWrapper.style.display = 'block'
// 变更标题信息
let header = this.element.querySelector('.easyuse-model-header')
if(header){
@@ -262,10 +195,90 @@ export class ModelInfoDialog extends ComfyDialog {
)
}
// 替换内容
let textarea = null
let notes = this.parseNote.call(this)
let editText = $t("✏️ Edit")
console.log(notes)
let textarea_div = $el("div.easyuse-model-detail-textarea",[
$el("p",notes?.length>0 ? notes : {textContent:$t('No notes')}),
])
if(!notes || notes.length == 0) textarea_div.classList.add('empty')
else textarea_div.classList.remove('empty')
this.info.replaceChildren(
$el("div.easyuse-model-detail",[
$el("div.easyuse-model-detail-head.flex-b",[
$el('span',$t("Notes")),
$el("a", {
textContent: editText,
href: "#",
style: {
fontSize: "12px",
float: "right",
color: "var(--warning-color)",
textDecoration: "none",
},
onclick: async (e) => {
e.preventDefault();
if (textarea) {
if(textarea.value != this.customNotes){
toast.showLoading($t('Saving Notes...'))
this.customNotes = textarea.value;
const resp = await api.fetchApi(
"/easyuse/metadata/notes/" + encodeURIComponent(`${this.type}/${this.name}`),
{
method: "POST",
body: this.customNotes,
}
);
toast.hideLoading()
if (resp.status !== 200) {
toast.error($t('Saving Failed'))
console.error(resp);
alert(`Error saving notes (${resp.status}) ${resp.statusText}`);
return;
}
toast.success($t('Saving Succeed'))
notes = this.parseNote.call(this)
console.log(notes)
textarea_div.replaceChildren($el("p",notes?.length>0 ? notes : {textContent:$t('No notes')}));
if(textarea.value) textarea_div.classList.remove('empty')
else textarea_div.classList.add('empty')
}else {
textarea_div.replaceChildren($el("p",{textContent:$t('No notes')}));
textarea_div.classList.add('empty')
}
e.target.textContent = editText;
textarea.remove();
textarea = null;
} else {
e.target.textContent = "💾 Save";
textarea = $el("textarea", {
placeholder: $t("Type your notes here"),
style: {
width: "100%",
minWidth: "200px",
minHeight: "50px",
height:"100px"
},
textContent: this.customNotes,
});
textarea_div.replaceChildren(textarea);
textarea.focus()
}
}
})
]),
textarea_div
]),
$el("div.easyuse-model-detail",[
$el("div.easyuse-model-detail-head",{textContent:$t("Details")}),
$el("div.easyuse-model-detail-body",[
$el("div.easyuse-model-detail-item",[
$el("div.easyuse-model-detail-item-label",{textContent:$t("Type")}),
$el("div.easyuse-model-detail-item-value",{textContent:info.model.type}),
]),
$el("div.easyuse-model-detail-item",[
$el("div.easyuse-model-detail-item-label",{textContent:$t("BaseModel")}),
$el("div.easyuse-model-detail-item-value",{textContent:info.baseModel}),
@@ -274,6 +287,10 @@ export class ModelInfoDialog extends ComfyDialog {
$el("div.easyuse-model-detail-item-label",{textContent:$t("Download")}),
$el("div.easyuse-model-detail-item-value",{textContent:info.stats?.downloadCount || 0}),
]),
$el("div.easyuse-model-detail-item",[
$el("div.easyuse-model-detail-item-label",{textContent:$t("Trained Words")}),
$el("div.easyuse-model-detail-item-value",{textContent:info?.trainedWords.join(',') || '-'}),
]),
$el("div.easyuse-model-detail-item",[
$el("div.easyuse-model-detail-item-label",{textContent:$t("Source")}),
$el("div.easyuse-model-detail-item-value",[
@@ -301,6 +318,7 @@ export class ModelInfoDialog extends ComfyDialog {
if (info.images?.length) {
this.imgCurrent = 0
this.isSaving = false
info.images.map(cate=>
cate.url &&
this.imgList.appendChild(
@@ -310,6 +328,9 @@ export class ModelInfoDialog extends ComfyDialog {
$el("div.save", {
textContent: "Save as preview",
onclick: async () => {
if(this.isSaving) return
this.isSaving = true
toast.showLoading($t('Saving Preview...'))
// Convert the preview to a blob
const blob = await (await fetch(cate.url)).blob();
@@ -326,6 +347,9 @@ export class ModelInfoDialog extends ComfyDialog {
});
if (resp.status !== 200) {
this.isSaving = false
toast.error($t('Saving Failed'))
toast.hideLoading()
console.error(resp);
alert(`Error saving preview (${req.status}) ${req.statusText}`);
return;
@@ -341,7 +365,11 @@ export class ModelInfoDialog extends ComfyDialog {
headers: {
"content-type": "application/json",
},
}).then(_=>{
toast.success($t('Saving Succeed'))
toast.hideLoading()
});
this.isSaving = false
app.refreshComboInNodes();
},
})
@@ -357,8 +385,12 @@ export class ModelInfoDialog extends ComfyDialog {
// 添加按钮
this.slideLeftButton = $el("button.left",{
parent: this.imgWrapper,
style:{
display:info.images.length <= 2 ? 'none' : 'block'
},
innerHTML:`<svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="transform: rotate(90deg);"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`,
onclick: ()=>{
if(info.images.length <= 2) return
_this.imgList.classList.remove("no-transition")
if(_this.imgCurrent == 0){
_this.imgCurrent = (info.images.length/2)-1
@@ -382,9 +414,14 @@ export class ModelInfoDialog extends ComfyDialog {
})
this.slideRightButton = $el("button.right",{
parent: this.imgWrapper,
style:{
display:info.images.length <= 2 ? 'none' : 'block'
},
innerHTML:`<svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="transform: rotate(-90deg);"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>`,
onclick: ()=>{
if(info.images.length <= 2) return
_this.imgList.classList.remove("no-transition")
if( _this.imgCurrent >= (info.images.length/2)-1){
_this.imgCurrent = 0
const max = info.images.length/2
@@ -410,31 +447,32 @@ export class ModelInfoDialog extends ComfyDialog {
}
if(info.description){
$el("div", {
parent: this.content,
innerHTML: info.description,
style: {
marginTop: "10px",
},
});
}
return info;
})
.catch((err) => {
this.imgWrapper.style.display = 'none'
content.textContent = "⚠️ " + err.message;
});
})
.finally(_=>{
})
}
}
export class CheckpointInfoDialog extends ModelInfoDialog {
async addInfo() {
super.addInfo();
const info = await this.addCivitaiInfo();
if (info) {
// this.addInfoEntry("Base Model", info.baseModel || "⚠️ Unknown");
//
// $el("div", {
// parent: this.content,
// innerHTML: info.description,
// style: {
// maxHeight: "250px",
// overflow: "auto",
// },
// });
}
// super.addInfo();
await this.addCivitaiInfo();
}
}
@@ -545,45 +583,45 @@ export class LoraInfoDialog extends ModelInfoDialog {
}
async addInfo() {
this.addInfoEntry("Name", this.metadata.ss_output_name || "⚠️ Unknown");
this.addInfoEntry("Base Model", this.metadata.ss_sd_model_name || "⚠️ Unknown");
this.addInfoEntry("Clip Skip", this.metadata.ss_clip_skip || "⚠️ Unknown");
// this.addInfoEntry("Name", this.metadata.ss_output_name || "⚠️ Unknown");
// this.addInfoEntry("Base Model", this.metadata.ss_sd_model_name || "⚠️ Unknown");
// this.addInfoEntry("Clip Skip", this.metadata.ss_clip_skip || "⚠️ Unknown");
//
// this.addInfoEntry(
// "Resolution",
// $el(
// "select",
// this.getResolutions().map((r) => $el("option", { textContent: r }))
// )
// );
this.addInfoEntry(
"Resolution",
$el(
"select",
this.getResolutions().map((r) => $el("option", { textContent: r }))
)
);
super.addInfo();
// super.addInfo();
const p = this.addCivitaiInfo();
this.addTags();
const info = await p;
if (info) {
$el(
"p",
{
parent: this.content,
textContent: "Trained Words: ",
},
[
$el("pre", {
textContent: info.trainedWords.join(", "),
style: {
whiteSpace: "pre-wrap",
margin: "10px 0",
background: "#222",
padding: "5px",
borderRadius: "5px",
maxHeight: "250px",
overflow: "auto",
},
}),
]
);
// $el(
// "p",
// {
// parent: this.content,
// textContent: "Trained Words: ",
// },
// [
// $el("pre", {
// textContent: info.trainedWords.join(", "),
// style: {
// whiteSpace: "pre-wrap",
// margin: "10px 0",
// background: "#222",
// padding: "5px",
// borderRadius: "5px",
// maxHeight: "250px",
// overflow: "auto",
// },
// }),
// ]
// );
$el("div", {
parent: this.content,
innerHTML: info.description,
+118
View File
@@ -0,0 +1,118 @@
import {sleep} from "./utils.js";
class Toast{
constructor() {
this.info_icon = `<svg focusable="false" data-icon="info-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"></path></svg>`
this.success_icon = `<svg focusable="false" data-icon="check-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"></path></svg>`
this.error_icon = `<svg focusable="false" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" fill-rule="evenodd" viewBox="64 64 896 896"><path d="M512 64c247.4 0 448 200.6 448 448S759.4 960 512 960 64 759.4 64 512 264.6 64 512 64zm127.98 274.82h-.04l-.08.06L512 466.75 384.14 338.88c-.04-.05-.06-.06-.08-.06a.12.12 0 00-.07 0c-.03 0-.05.01-.09.05l-45.02 45.02a.2.2 0 00-.05.09.12.12 0 000 .07v.02a.27.27 0 00.06.06L466.75 512 338.88 639.86c-.05.04-.06.06-.06.08a.12.12 0 000 .07c0 .03.01.05.05.09l45.02 45.02a.2.2 0 00.09.05.12.12 0 00.07 0c.02 0 .04-.01.08-.05L512 557.25l127.86 127.87c.04.04.06.05.08.05a.12.12 0 00.07 0c.03 0 .05-.01.09-.05l45.02-45.02a.2.2 0 00.05-.09.12.12 0 000-.07v-.02a.27.27 0 00-.05-.06L557.25 512l127.87-127.86c.04-.04.05-.06.05-.08a.12.12 0 000-.07c0-.03-.01-.05-.05-.09l-45.02-45.02a.2.2 0 00-.09-.05.12.12 0 00-.07 0z"></path></svg>`
this.warn_icon = `<svg focusable="false" data-icon="exclamation-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"></path></svg>`
this.loading_icon = `<svg focusable="false" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg>`
}
async showToast(data){
let container = document.querySelector(".easyuse-toast-container");
if (!container) {
container = document.createElement("div");
container.classList.add("easyuse-toast-container");
document.body.appendChild(container);
}
await this.hideToast(data.id);
const toastContainer = document.createElement("div");
const content = document.createElement("span");
content.innerHTML = data.content;
toastContainer.appendChild(content);
for (let a = 0; a < (data.actions || []).length; a++) {
const action = data.actions[a];
if (a > 0) {
const sep = document.createElement("span");
sep.innerHTML = "&nbsp;|&nbsp;";
toastContainer.appendChild(sep);
}
const actionEl = document.createElement("a");
actionEl.innerText = action.label;
if (action.href) {
actionEl.target = "_blank";
actionEl.href = action.href;
}
if (action.callback) {
actionEl.onclick = (e) => {
return action.callback(e);
};
}
toastContainer.appendChild(actionEl);
}
const animContainer = document.createElement("div");
animContainer.setAttribute("toast-id", data.id);
animContainer.appendChild(toastContainer);
container.appendChild(animContainer);
await sleep(64);
animContainer.style.marginTop = `-${animContainer.offsetHeight}px`;
await sleep(64);
animContainer.classList.add("-show");
if (data.duration) {
await sleep(data.duration);
this.hideToast(data.id);
}
}
async hideToast(id) {
const msg = document.querySelector(`.easyuse-toast-container > [toast-id="${id}"]`);
if (msg === null || msg === void 0 ? void 0 : msg.classList.contains("-show")) {
msg.classList.remove("-show");
await sleep(750);
}
msg && msg.remove();
}
async clearAllMessages() {
let container = document.querySelector(".easyuse-toast-container");
container && (container.innerHTML = "");
}
async info(content, duration = 3000, actions = []) {
this.showToast({
id: `toast-info`,
content: `${this.info_icon} ${content}`,
duration,
actions
});
}
async success(content, duration = 3000, actions = []) {
this.showToast({
id: `toast-success`,
content: `${this.success_icon} ${content}`,
duration,
actions
});
}
async error(content, duration = 3000, actions = []) {
this.showToast({
id: `toast-error`,
content: `${this.error_icon} ${content}`,
duration,
actions
});
}
async warn(content, duration = 3000, actions = []) {
this.showToast({
id: `toast-warn`,
content: `${this.warn_icon} ${content}`,
duration,
actions
});
}
async showLoading(content, duration = 0, actions = []) {
this.showToast({
id: `toast-loading`,
content: `${this.loading_icon} ${content}`,
duration,
actions
});
}
async hideLoading() {
this.hideToast("toast-loading");
}
}
export const toast = new Toast();
+7 -1
View File
@@ -1,3 +1,10 @@
export function sleep(ms = 100, value) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(value);
}, ms);
});
}
export function addPreconnect(href, crossorigin=false){
const preconnect = document.createElement("link");
preconnect.rel = 'preconnect'
@@ -5,7 +12,6 @@ export function addPreconnect(href, crossorigin=false){
if(crossorigin) preconnect.crossorigin = ''
document.head.appendChild(preconnect);
}
export function addCss(href, base=true) {
const link = document.createElement("link");
link.rel = "stylesheet";
+17
View File
@@ -1,9 +1,26 @@
import { api } from "/scripts/api.js";
import { app } from "/scripts/app.js";
import {deepEqual,addCss} from "../common/utils.js";
import {$t} from '../common/i18n.js';
import {toast} from "../common/toast.js";
addCss('css/index.css')
api.addEventListener("easyuse-toast",event=>{
const content = event.detail.content
const type = event.detail.type
const duration = event.detail.duration
if(!type){
toast.info(content, duration)
}
else{
toast.showToast({
id: `toast-${type}`,
content: `${toast[type+"_icon"]} ${content}`,
duration: duration || 3000,
})
}
})
app.registerExtension({
name: "comfy.easyUse",
init() {
+17 -17
View File
@@ -267,23 +267,23 @@ const addMenu = (content, type, nodes_include, nodeType, has_submenu=true) => {
callback: (value, options, e, menu, node) => showSwapMenu(value, options, e, menu, node, type, nodes_include)
})
if(type == 'loaders'){
// options.unshift({
// content: $t("💎 View Lora Info..."),
// callback: (value, options, e, menu, node) => {
// const widget = node.widgets.find(cate=> cate.name == 'lora_name')
// let name = widget.value;
// if (!name || name == 'None') return
// new LoraInfoDialog(name).show('loras', name);
// }
// })
// options.unshift({
// content: $t("💎 View Checkpoint Info..."),
// callback: (value, options, e, menu, node) => {
// let name = node.widgets[0].value;
// if (!name || name == 'None') return
// new CheckpointInfoDialog(name).show('checkpoints', name);
// }
// })
options.unshift({
content: $t("💎 View Lora Info..."),
callback: (value, options, e, menu, node) => {
const widget = node.widgets.find(cate=> cate.name == 'lora_name')
let name = widget.value;
if (!name || name == 'None') return
new LoraInfoDialog(name).show('loras', name);
}
})
options.unshift({
content: $t("💎 View Checkpoint Info..."),
callback: (value, options, e, menu, node) => {
let name = node.widgets[0].value;
if (!name || name == 'None') return
new CheckpointInfoDialog(name).show('checkpoints', name);
}
})
}
})
}