mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-02-10 05:22:34 +08:00
Merge pull request #2 from xbol0/ext_i18n
[feature] i18n extension support
This commit is contained in:
commit
f4190fc9af
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,3 +14,4 @@ venv/
|
|||||||
web/extensions/*
|
web/extensions/*
|
||||||
!web/extensions/logging.js.example
|
!web/extensions/logging.js.example
|
||||||
!web/extensions/core/
|
!web/extensions/core/
|
||||||
|
!web/extensions/i18n/
|
||||||
|
|||||||
@ -535,7 +535,7 @@ def should_use_fp16(device=None, model_params=0):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
#FP16 is just broken on these cards
|
#FP16 is just broken on these cards
|
||||||
nvidia_16_series = ["1660", "1650", "1630", "T500", "T550", "T600", "MX550", "MX450"]
|
nvidia_16_series = ["1660", "1650", "1630", "T500", "T550", "T600", "MX550", "MX450", "CMP 30HX"]
|
||||||
for x in nvidia_16_series:
|
for x in nvidia_16_series:
|
||||||
if x in props.name:
|
if x in props.name:
|
||||||
return False
|
return False
|
||||||
|
|||||||
26
comfy/sd.py
26
comfy/sd.py
@ -70,13 +70,22 @@ def load_lora(lora, to_load):
|
|||||||
alpha = lora[alpha_name].item()
|
alpha = lora[alpha_name].item()
|
||||||
loaded_keys.add(alpha_name)
|
loaded_keys.add(alpha_name)
|
||||||
|
|
||||||
A_name = "{}.lora_up.weight".format(x)
|
regular_lora = "{}.lora_up.weight".format(x)
|
||||||
B_name = "{}.lora_down.weight".format(x)
|
diffusers_lora = "{}_lora.up.weight".format(x)
|
||||||
mid_name = "{}.lora_mid.weight".format(x)
|
A_name = None
|
||||||
|
|
||||||
if A_name in lora.keys():
|
if regular_lora in lora.keys():
|
||||||
|
A_name = regular_lora
|
||||||
|
B_name = "{}.lora_down.weight".format(x)
|
||||||
|
mid_name = "{}.lora_mid.weight".format(x)
|
||||||
|
elif diffusers_lora in lora.keys():
|
||||||
|
A_name = diffusers_lora
|
||||||
|
B_name = "{}_lora.down.weight".format(x)
|
||||||
|
mid_name = None
|
||||||
|
|
||||||
|
if A_name is not None:
|
||||||
mid = None
|
mid = None
|
||||||
if mid_name in lora.keys():
|
if mid_name is not None and mid_name in lora.keys():
|
||||||
mid = lora[mid_name]
|
mid = lora[mid_name]
|
||||||
loaded_keys.add(mid_name)
|
loaded_keys.add(mid_name)
|
||||||
patch_dict[to_load[x]] = (lora[A_name], lora[B_name], alpha, mid)
|
patch_dict[to_load[x]] = (lora[A_name], lora[B_name], alpha, mid)
|
||||||
@ -202,6 +211,11 @@ def model_lora_keys_unet(model, key_map={}):
|
|||||||
if k.endswith(".weight"):
|
if k.endswith(".weight"):
|
||||||
key_lora = k[:-len(".weight")].replace(".", "_")
|
key_lora = k[:-len(".weight")].replace(".", "_")
|
||||||
key_map["lora_unet_{}".format(key_lora)] = "diffusion_model.{}".format(diffusers_keys[k])
|
key_map["lora_unet_{}".format(key_lora)] = "diffusion_model.{}".format(diffusers_keys[k])
|
||||||
|
|
||||||
|
diffusers_lora_key = "unet.{}".format(k[:-len(".weight")].replace(".to_", ".processor.to_"))
|
||||||
|
if diffusers_lora_key.endswith(".to_out.0"):
|
||||||
|
diffusers_lora_key = diffusers_lora_key[:-2]
|
||||||
|
key_map[diffusers_lora_key] = "diffusion_model.{}".format(diffusers_keys[k])
|
||||||
return key_map
|
return key_map
|
||||||
|
|
||||||
def set_attr(obj, attr, value):
|
def set_attr(obj, attr, value):
|
||||||
@ -864,7 +878,7 @@ def load_controlnet(ckpt_path, model=None):
|
|||||||
use_fp16 = model_management.should_use_fp16()
|
use_fp16 = model_management.should_use_fp16()
|
||||||
controlnet_config = model_detection.model_config_from_unet(controlnet_data, prefix, use_fp16).unet_config
|
controlnet_config = model_detection.model_config_from_unet(controlnet_data, prefix, use_fp16).unet_config
|
||||||
controlnet_config.pop("out_channels")
|
controlnet_config.pop("out_channels")
|
||||||
controlnet_config["hint_channels"] = 3
|
controlnet_config["hint_channels"] = controlnet_data["{}input_hint_block.0.weight".format(prefix)].shape[1]
|
||||||
control_model = cldm.ControlNet(**controlnet_config)
|
control_model = cldm.ControlNet(**controlnet_config)
|
||||||
|
|
||||||
if pth:
|
if pth:
|
||||||
|
|||||||
@ -40,7 +40,8 @@ def cuda_malloc_supported():
|
|||||||
blacklist = {"GeForce GTX TITAN X", "GeForce GTX 980", "GeForce GTX 970", "GeForce GTX 960", "GeForce GTX 950", "GeForce 945M",
|
blacklist = {"GeForce GTX TITAN X", "GeForce GTX 980", "GeForce GTX 970", "GeForce GTX 960", "GeForce GTX 950", "GeForce 945M",
|
||||||
"GeForce 940M", "GeForce 930M", "GeForce 920M", "GeForce 910M", "GeForce GTX 750", "GeForce GTX 745", "Quadro K620",
|
"GeForce 940M", "GeForce 930M", "GeForce 920M", "GeForce 910M", "GeForce GTX 750", "GeForce GTX 745", "Quadro K620",
|
||||||
"Quadro K1200", "Quadro K2200", "Quadro M500", "Quadro M520", "Quadro M600", "Quadro M620", "Quadro M1000",
|
"Quadro K1200", "Quadro K2200", "Quadro M500", "Quadro M520", "Quadro M600", "Quadro M620", "Quadro M1000",
|
||||||
"Quadro M1200", "Quadro M2000", "Quadro M2200", "Quadro M3000", "Quadro M4000", "Quadro M5000", "Quadro M5500", "Quadro M6000"}
|
"Quadro M1200", "Quadro M2000", "Quadro M2200", "Quadro M3000", "Quadro M4000", "Quadro M5000", "Quadro M5500", "Quadro M6000",
|
||||||
|
"GeForce MX110", "GeForce MX130", "GeForce 830M", "GeForce 840M", "GeForce GTX 850M", "GeForce GTX 860M"}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
names = get_gpu_names()
|
names = get_gpu_names()
|
||||||
|
|||||||
@ -159,13 +159,64 @@
|
|||||||
"\n"
|
"\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {
|
||||||
|
"id": "kkkkkkkkkkkkkkk"
|
||||||
|
},
|
||||||
|
"source": [
|
||||||
|
"### Run ComfyUI with cloudflared (Recommended Way)\n",
|
||||||
|
"\n",
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"id": "jjjjjjjjjjjjjj"
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"!wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb\n",
|
||||||
|
"!dpkg -i cloudflared-linux-amd64.deb\n",
|
||||||
|
"\n",
|
||||||
|
"import subprocess\n",
|
||||||
|
"import threading\n",
|
||||||
|
"import time\n",
|
||||||
|
"import socket\n",
|
||||||
|
"import urllib.request\n",
|
||||||
|
"\n",
|
||||||
|
"def iframe_thread(port):\n",
|
||||||
|
" while True:\n",
|
||||||
|
" time.sleep(0.5)\n",
|
||||||
|
" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n",
|
||||||
|
" result = sock.connect_ex(('127.0.0.1', port))\n",
|
||||||
|
" if result == 0:\n",
|
||||||
|
" break\n",
|
||||||
|
" sock.close()\n",
|
||||||
|
" print(\"\\nComfyUI finished loading, trying to launch cloudflared (if it gets stuck here cloudflared is having issues)\\n\")\n",
|
||||||
|
"\n",
|
||||||
|
" p = subprocess.Popen([\"cloudflared\", \"tunnel\", \"--url\", \"http://127.0.0.1:{}\".format(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
|
||||||
|
" for line in p.stderr:\n",
|
||||||
|
" l = line.decode()\n",
|
||||||
|
" if \"trycloudflare.com \" in l:\n",
|
||||||
|
" print(\"This is the URL to access ComfyUI:\", l[l.find(\"http\"):], end='')\n",
|
||||||
|
" #print(l, end='')\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"threading.Thread(target=iframe_thread, daemon=True, args=(8188,)).start()\n",
|
||||||
|
"\n",
|
||||||
|
"!python main.py --dont-print-server"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"id": "kkkkkkkkkkkkkk"
|
"id": "kkkkkkkkkkkkkk"
|
||||||
},
|
},
|
||||||
"source": [
|
"source": [
|
||||||
"### Run ComfyUI with localtunnel (Recommended Way)\n",
|
"### Run ComfyUI with localtunnel\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n"
|
"\n"
|
||||||
]
|
]
|
||||||
|
|||||||
@ -345,6 +345,11 @@ class PromptServer():
|
|||||||
vram_total, torch_vram_total = comfy.model_management.get_total_memory(device, torch_total_too=True)
|
vram_total, torch_vram_total = comfy.model_management.get_total_memory(device, torch_total_too=True)
|
||||||
vram_free, torch_vram_free = comfy.model_management.get_free_memory(device, torch_free_too=True)
|
vram_free, torch_vram_free = comfy.model_management.get_free_memory(device, torch_free_too=True)
|
||||||
system_stats = {
|
system_stats = {
|
||||||
|
"system": {
|
||||||
|
"os": os.name,
|
||||||
|
"python_version": sys.version,
|
||||||
|
"embedded_python": os.path.split(os.path.split(sys.executable)[0])[1] == "python_embeded"
|
||||||
|
},
|
||||||
"devices": [
|
"devices": [
|
||||||
{
|
{
|
||||||
"name": device_name,
|
"name": device_name,
|
||||||
|
|||||||
@ -435,7 +435,7 @@ app.registerExtension({
|
|||||||
$el("td", [
|
$el("td", [
|
||||||
$el("label", {
|
$el("label", {
|
||||||
for: id.replaceAll(".", "-"),
|
for: id.replaceAll(".", "-"),
|
||||||
textContent: "Color palette",
|
textContent: i18next.t("settings.Comfy.ColorPalette"),
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
$el("td", [
|
$el("td", [
|
||||||
@ -449,7 +449,7 @@ app.registerExtension({
|
|||||||
}, [
|
}, [
|
||||||
$el("input", {
|
$el("input", {
|
||||||
type: "button",
|
type: "button",
|
||||||
value: "Export",
|
value: i18next.t("settings.Comfy.ColorPalette.export"),
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
const colorPaletteId = app.ui.settings.getSettingValue(id, defaultColorPaletteId);
|
const colorPaletteId = app.ui.settings.getSettingValue(id, defaultColorPaletteId);
|
||||||
const colorPalette = await completeColorPalette(getColorPalette(colorPaletteId));
|
const colorPalette = await completeColorPalette(getColorPalette(colorPaletteId));
|
||||||
@ -471,14 +471,14 @@ app.registerExtension({
|
|||||||
}),
|
}),
|
||||||
$el("input", {
|
$el("input", {
|
||||||
type: "button",
|
type: "button",
|
||||||
value: "Import",
|
value: i18next.t("settings.Comfy.ColorPalette.import"),
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
fileInput.click();
|
fileInput.click();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$el("input", {
|
$el("input", {
|
||||||
type: "button",
|
type: "button",
|
||||||
value: "Template",
|
value: i18next.t("settings.Comfy.ColorPalette.template"),
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
const colorPalette = await getColorPaletteTemplate();
|
const colorPalette = await getColorPaletteTemplate();
|
||||||
const json = JSON.stringify(colorPalette, null, 2); // convert the data to a JSON string
|
const json = JSON.stringify(colorPalette, null, 2); // convert the data to a JSON string
|
||||||
@ -499,7 +499,7 @@ app.registerExtension({
|
|||||||
}),
|
}),
|
||||||
$el("input", {
|
$el("input", {
|
||||||
type: "button",
|
type: "button",
|
||||||
value: "Delete",
|
value: i18next.t("settings.Comfy.ColorPalette.delete"),
|
||||||
onclick: async () => {
|
onclick: async () => {
|
||||||
let colorPaletteId = app.ui.settings.getSettingValue(id, defaultColorPaletteId);
|
let colorPaletteId = app.ui.settings.getSettingValue(id, defaultColorPaletteId);
|
||||||
|
|
||||||
|
|||||||
@ -27,10 +27,13 @@ const ext = {
|
|||||||
const clickedComboValue = currentNode.widgets
|
const clickedComboValue = currentNode.widgets
|
||||||
.filter(w => w.type === "combo" && w.options.values.length === values.length)
|
.filter(w => w.type === "combo" && w.options.values.length === values.length)
|
||||||
.find(w => w.options.values.every((v, i) => v === values[i]))
|
.find(w => w.options.values.every((v, i) => v === values[i]))
|
||||||
.value;
|
?.value;
|
||||||
|
|
||||||
let selectedIndex = values.findIndex(v => v === clickedComboValue);
|
let selectedIndex = clickedComboValue ? values.findIndex(v => v === clickedComboValue) : 0;
|
||||||
let selectedItem = displayedItems?.[selectedIndex];
|
if (selectedIndex < 0) {
|
||||||
|
selectedIndex = 0;
|
||||||
|
}
|
||||||
|
let selectedItem = displayedItems[selectedIndex];
|
||||||
updateSelected();
|
updateSelected();
|
||||||
|
|
||||||
// Apply highlighting to the selected item
|
// Apply highlighting to the selected item
|
||||||
|
|||||||
46
web/extensions/core/i18n.js
Normal file
46
web/extensions/core/i18n.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { app } from "../../scripts/app.js";
|
||||||
|
|
||||||
|
app.registerExtension({
|
||||||
|
name: "i18next",
|
||||||
|
addCustomNodeDefs(defs) {
|
||||||
|
for (const k in defs) {
|
||||||
|
defs[k].display_name = i18next.t(`node.title.${k}`)
|
||||||
|
|
||||||
|
if ("input" in defs[k] && defs[k].input.required) {
|
||||||
|
for (const i in defs[k].input.required) {
|
||||||
|
if (defs[k].input.required[i].length > 1) {
|
||||||
|
defs[k].input.required[i][1].label = i18next.t(`node.input.${k}.${i}`)
|
||||||
|
} else {
|
||||||
|
defs[k].input.required[i].push({ label: i18next.t(`node.input.${k}.${i}`) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeCreated(node) {
|
||||||
|
if ("inputs" in node) {
|
||||||
|
for (const item of node.inputs) {
|
||||||
|
item.label = i18next.t(`node.input.${node.comfyClass}.${item.name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("widgets" in node) {
|
||||||
|
for (const item of node.widgets) {
|
||||||
|
item.label = i18next.t(`node.input.${node.comfyClass}.${item.name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("outputs" in node) {
|
||||||
|
for (const item of node.outputs) {
|
||||||
|
item.label = i18next.t(`node.output.${node.comfyClass}.${item.name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
afterNodesRegistrations() {
|
||||||
|
const defs = LiteGraph.registered_node_types
|
||||||
|
|
||||||
|
for (const k in defs) {
|
||||||
|
defs[k].category = i18next.t(`category.${defs[k].category}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
25
web/extensions/core/linkRenderMode.js
Normal file
25
web/extensions/core/linkRenderMode.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { app } from "/scripts/app.js";
|
||||||
|
|
||||||
|
const id = "Comfy.LinkRenderMode";
|
||||||
|
const ext = {
|
||||||
|
name: id,
|
||||||
|
async setup(app) {
|
||||||
|
app.ui.settings.addSetting({
|
||||||
|
id,
|
||||||
|
name: "Link Render Mode",
|
||||||
|
defaultValue: 2,
|
||||||
|
type: "combo",
|
||||||
|
options: LiteGraph.LINK_RENDER_MODES.map((m, i) => ({
|
||||||
|
value: i,
|
||||||
|
text: m,
|
||||||
|
selected: i == app.canvas.links_render_mode,
|
||||||
|
})),
|
||||||
|
onChange(value) {
|
||||||
|
app.canvas.links_render_mode = +value;
|
||||||
|
app.graph.setDirtyCanvas(true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
app.registerExtension(ext);
|
||||||
@ -57,6 +57,7 @@ function convertToInput(node, widget, config) {
|
|||||||
const sz = node.size;
|
const sz = node.size;
|
||||||
node.addInput(widget.name, linkType, {
|
node.addInput(widget.name, linkType, {
|
||||||
widget: { name: widget.name, config },
|
widget: { name: widget.name, config },
|
||||||
|
label: config?.[1] ? i18next.t(config?.[1].label) : void 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const widget of node.widgets) {
|
for (const widget of node.widgets) {
|
||||||
|
|||||||
125
web/i18n/en_US.js
Normal file
125
web/i18n/en_US.js
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
export default {
|
||||||
|
translation: {
|
||||||
|
"ui.queue_btn": "Queue Prompt",
|
||||||
|
"ui.queue_front_btn": "Queue Front",
|
||||||
|
"ui.view_queue_btn": "View Queue",
|
||||||
|
"ui.view_history_btn": "View History",
|
||||||
|
"ui.save_btn": "Save",
|
||||||
|
"ui.load_btn": "Load",
|
||||||
|
"ui.refresh_btn": "Refresh",
|
||||||
|
"ui.clipspace_btn": "Clipspace",
|
||||||
|
"ui.clear_btn": "Clear",
|
||||||
|
"ui.load_default_btn": "Load Default",
|
||||||
|
"ui.close_btn": "Close",
|
||||||
|
"ui.queue_size": "Queue size: ",
|
||||||
|
"ui.extra_options": "Extra options",
|
||||||
|
|
||||||
|
"ui.settings.title": "Settings",
|
||||||
|
|
||||||
|
"ui.canvas_menu_add_node": "Add Node",
|
||||||
|
"ui.canvas_menu_add_group": "Add Group",
|
||||||
|
|
||||||
|
"ui.node_panel.header.properties": "Properties",
|
||||||
|
"ui.node_panel.header.title": "Title",
|
||||||
|
"ui.node_panel.header.mode": "Mode",
|
||||||
|
"ui.node_panel.header.color": "Color",
|
||||||
|
|
||||||
|
"node.title.KSampler": "KSampler",
|
||||||
|
"node.title.KSamplerAdvanced": "KSampler (Advanced)",
|
||||||
|
// Loaders
|
||||||
|
"node.title.CheckpointLoader": "Load Checkpoint (With Config)",
|
||||||
|
"node.title.CheckpointLoaderSimple": "Load Checkpoint",
|
||||||
|
"node.title.VAELoader": "Load VAE",
|
||||||
|
"node.title.LoraLoader": "Load LoRA",
|
||||||
|
"node.title.CLIPLoader": "Load CLIP",
|
||||||
|
"node.title.ControlNetLoader": "Load ControlNet Model",
|
||||||
|
"node.title.DiffControlNetLoader": "Load ControlNet Model (diff)",
|
||||||
|
"node.title.StyleModelLoader": "Load Style Model",
|
||||||
|
"node.title.CLIPVisionLoader": "Load CLIP Vision",
|
||||||
|
"node.title.UpscaleModelLoader": "Load Upscale Model",
|
||||||
|
// Conditioning
|
||||||
|
"node.title.CLIPVisionEncode": "CLIP Vision Encode",
|
||||||
|
"node.title.StyleModelApply": "Apply Style Model",
|
||||||
|
"node.title.CLIPTextEncode": "CLIP Text Encode (Prompt)",
|
||||||
|
"node.title.CLIPSetLastLayer": "CLIP Set Last Layer",
|
||||||
|
"node.title.ConditioningCombine": "Conditioning (Combine)",
|
||||||
|
"node.title.ConditioningAverage ": "Conditioning (Average)",
|
||||||
|
"node.title.ConditioningConcat": "Conditioning (Concat)",
|
||||||
|
"node.title.ConditioningSetArea": "Conditioning (Set Area)",
|
||||||
|
"node.title.ConditioningSetMask": "Conditioning (Set Mask)",
|
||||||
|
"node.title.ControlNetApply": "Apply ControlNet",
|
||||||
|
"node.title.ControlNetApplyAdvanced": "Apply ControlNet (Advanced)",
|
||||||
|
// Latent
|
||||||
|
"node.title.VAEEncodeForInpaint": "VAE Encode (for Inpainting)",
|
||||||
|
"node.title.SetLatentNoiseMask": "Set Latent Noise Mask",
|
||||||
|
"node.title.VAEDecode": "VAE Decode",
|
||||||
|
"node.title.VAEEncode": "VAE Encode",
|
||||||
|
"node.title.LatentRotate": "Rotate Latent",
|
||||||
|
"node.title.LatentFlip": "Flip Latent",
|
||||||
|
"node.title.LatentCrop": "Crop Latent",
|
||||||
|
"node.title.EmptyLatentImage": "Empty Latent Image",
|
||||||
|
"node.title.LatentUpscale": "Upscale Latent",
|
||||||
|
"node.title.LatentUpscaleBy": "Upscale Latent By",
|
||||||
|
"node.title.LatentComposite": "Latent Composite",
|
||||||
|
"node.title.LatentBlend": "Latent Blend",
|
||||||
|
"LatentFromBatch": "Latent From Batch",
|
||||||
|
"node.title.RepeatLatentBatch": "Repeat Latent Batch",
|
||||||
|
// Image
|
||||||
|
"node.title.SaveImage": "Save Image",
|
||||||
|
"node.title.PreviewImage": "Preview Image",
|
||||||
|
"node.title.LoadImage": "Load Image",
|
||||||
|
"node.title.LoadImageMask": "Load Image (as Mask)",
|
||||||
|
"node.title.ImageScale": "Upscale Image",
|
||||||
|
"node.title.ImageScaleBy": "Upscale Image By",
|
||||||
|
"node.title.ImageUpscaleWithModel": "Upscale Image (using Model)",
|
||||||
|
"node.title.ImageInvert": "Invert Image",
|
||||||
|
"node.title.ImagePadForOutpaint": "Pad Image for Outpainting",
|
||||||
|
// _for_testing
|
||||||
|
"node.title.VAEDecodeTiled": "VAE Decode (Tiled)",
|
||||||
|
"node.title.VAEEncodeTiled": "VAE Encode (Tiled)",
|
||||||
|
|
||||||
|
"node.input.SaveImage.filename_prefix": "filename_prefix",
|
||||||
|
"node.input.SaveImage.images": "images",
|
||||||
|
|
||||||
|
"node.output.CheckpointLoaderSimple.MODEL": "MODEL",
|
||||||
|
|
||||||
|
"category.conditioning": "conditioning",
|
||||||
|
"category.loaders": "loaders",
|
||||||
|
"category.latent": "latent",
|
||||||
|
"category.latent/inpaint": "latent/inpaint",
|
||||||
|
"category.latent/batch": "latent/batch",
|
||||||
|
"category.image": "image",
|
||||||
|
"category.mask": "mask",
|
||||||
|
"category.image/upscaling": "image/upscaling",
|
||||||
|
"category.sampling": "sampling",
|
||||||
|
"category._for_testing": "_for_testing",
|
||||||
|
"category.latent/transform": "latent/transform",
|
||||||
|
"category.advanced/loaders": "advanced/loaders",
|
||||||
|
"category.conditioning/style_model": "conditioning/style_model",
|
||||||
|
"category.conditioning/gligen": "conditioning/gligen",
|
||||||
|
"category.advanced/loaders/deprecated": "advanced/loaders/deprecated",
|
||||||
|
"category.advanced/conditioning": "advanced/conditioning",
|
||||||
|
"category.image/postprocessing": "image/postprocessing",
|
||||||
|
"category.advanced/model_merging": "advanced/model_merging",
|
||||||
|
"category.image/preprocessors": "image/preprocessors",
|
||||||
|
"category.utils": "utils",
|
||||||
|
|
||||||
|
"settings.Comfy.ConfirmClear": "Require confirmation when clearing workflow",
|
||||||
|
"settings.Comfy.PromptFilename": "Prompt for filename when saving workflow",
|
||||||
|
"settings.Comfy.PreviewFormat": "When displaying a preview in the image widget, convert it to a lightweight image, e.g. webp, jpeg, webp;50, etc.",
|
||||||
|
"settings.Comfy.DisableSliders": "Disable sliders.",
|
||||||
|
"settings.Comfy.DevMode": "Enable Dev mode Options",
|
||||||
|
"settings.Comfy.ColorPalette": "Color Palette",
|
||||||
|
"settings.Comfy.EditAttention.Delta": "Ctrl+up/down precision",
|
||||||
|
"settings.Comfy.InvertMenuScrolling": "Invert Menu Scrolling",
|
||||||
|
"settings.Comfy.LinkRenderMode": "Link Render Mode",
|
||||||
|
"settings.Comfy.NodeSuggestions.number": "Number of nodes suggestions",
|
||||||
|
"settings.Comfy.SnapToGrid.GridSize": "Grid Size",
|
||||||
|
"settings.Comfy.Logging.Enabled": "Comfy.Logging.Enabled",
|
||||||
|
"settings.Comfy.MenuPosition": "Save menu position",
|
||||||
|
"settings.Comfy.ColorPalette.export": "Export",
|
||||||
|
"settings.Comfy.ColorPalette.import": "Import",
|
||||||
|
"settings.Comfy.ColorPalette.template": "Template",
|
||||||
|
"settings.Comfy.ColorPalette.delete": "Delete",
|
||||||
|
}
|
||||||
|
}
|
||||||
125
web/i18n/zh_CN.js
Normal file
125
web/i18n/zh_CN.js
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
export default {
|
||||||
|
translation: {
|
||||||
|
"ui.queue_btn": "冲冲冲",
|
||||||
|
"ui.queue_front_btn": "插队冲冲冲",
|
||||||
|
"ui.view_queue_btn": "查看队列",
|
||||||
|
"ui.view_history_btn": "查看历史",
|
||||||
|
"ui.save_btn": "保存",
|
||||||
|
"ui.load_btn": "加载",
|
||||||
|
"ui.refresh_btn": "刷新",
|
||||||
|
"ui.clipspace_btn": "Clipspace",
|
||||||
|
"ui.clear_btn": "清空",
|
||||||
|
"ui.load_default_btn": "加载默认配置",
|
||||||
|
"ui.close_btn": "关闭",
|
||||||
|
"ui.queue_size": "队列数量: ",
|
||||||
|
"ui.extra_options": "额外选项",
|
||||||
|
|
||||||
|
"ui.settings.title": "设置",
|
||||||
|
|
||||||
|
"ui.canvas_menu_add_node": "添加节点",
|
||||||
|
"ui.canvas_menu_add_group": "添加组",
|
||||||
|
|
||||||
|
"ui.node_panel.header.properties": "属性",
|
||||||
|
"ui.node_panel.header.title": "标题",
|
||||||
|
"ui.node_panel.header.mode": "模式",
|
||||||
|
"ui.node_panel.header.color": "颜色",
|
||||||
|
|
||||||
|
"node.title.KSampler": "采样器",
|
||||||
|
"node.title.KSamplerAdvanced": "采样器 (高级)",
|
||||||
|
// Loaders
|
||||||
|
"node.title.CheckpointLoader": "Load Checkpoint (With Config)",
|
||||||
|
"node.title.CheckpointLoaderSimple": "加载模型",
|
||||||
|
"node.title.VAELoader": "Load VAE",
|
||||||
|
"node.title.LoraLoader": "Load LoRA",
|
||||||
|
"node.title.CLIPLoader": "Load CLIP",
|
||||||
|
"node.title.ControlNetLoader": "Load ControlNet Model",
|
||||||
|
"node.title.DiffControlNetLoader": "Load ControlNet Model (diff)",
|
||||||
|
"node.title.StyleModelLoader": "Load Style Model",
|
||||||
|
"node.title.CLIPVisionLoader": "Load CLIP Vision",
|
||||||
|
"node.title.UpscaleModelLoader": "Load Upscale Model",
|
||||||
|
// Conditioning
|
||||||
|
"node.title.CLIPVisionEncode": "CLIP Vision Encode",
|
||||||
|
"node.title.StyleModelApply": "Apply Style Model",
|
||||||
|
"node.title.CLIPTextEncode": "CLIP Text Encode (Prompt)",
|
||||||
|
"node.title.CLIPSetLastLayer": "CLIP Set Last Layer",
|
||||||
|
"node.title.ConditioningCombine": "Conditioning (Combine)",
|
||||||
|
"node.title.ConditioningAverage ": "Conditioning (Average)",
|
||||||
|
"node.title.ConditioningConcat": "Conditioning (Concat)",
|
||||||
|
"node.title.ConditioningSetArea": "Conditioning (Set Area)",
|
||||||
|
"node.title.ConditioningSetMask": "Conditioning (Set Mask)",
|
||||||
|
"node.title.ControlNetApply": "Apply ControlNet",
|
||||||
|
"node.title.ControlNetApplyAdvanced": "Apply ControlNet (Advanced)",
|
||||||
|
// Latent
|
||||||
|
"node.title.VAEEncodeForInpaint": "VAE Encode (for Inpainting)",
|
||||||
|
"node.title.SetLatentNoiseMask": "Set Latent Noise Mask",
|
||||||
|
"node.title.VAEDecode": "VAE Decode",
|
||||||
|
"node.title.VAEEncode": "VAE Encode",
|
||||||
|
"node.title.LatentRotate": "Rotate Latent",
|
||||||
|
"node.title.LatentFlip": "Flip Latent",
|
||||||
|
"node.title.LatentCrop": "Crop Latent",
|
||||||
|
"node.title.EmptyLatentImage": "Empty Latent Image",
|
||||||
|
"node.title.LatentUpscale": "Upscale Latent",
|
||||||
|
"node.title.LatentUpscaleBy": "Upscale Latent By",
|
||||||
|
"node.title.LatentComposite": "Latent Composite",
|
||||||
|
"node.title.LatentBlend": "Latent Blend",
|
||||||
|
"LatentFromBatch": "Latent From Batch",
|
||||||
|
"node.title.RepeatLatentBatch": "Repeat Latent Batch",
|
||||||
|
// Image
|
||||||
|
"node.title.SaveImage": "Save Image",
|
||||||
|
"node.title.PreviewImage": "Preview Image",
|
||||||
|
"node.title.LoadImage": "Load Image",
|
||||||
|
"node.title.LoadImageMask": "Load Image (as Mask)",
|
||||||
|
"node.title.ImageScale": "Upscale Image",
|
||||||
|
"node.title.ImageScaleBy": "Upscale Image By",
|
||||||
|
"node.title.ImageUpscaleWithModel": "Upscale Image (using Model)",
|
||||||
|
"node.title.ImageInvert": "Invert Image",
|
||||||
|
"node.title.ImagePadForOutpaint": "Pad Image for Outpainting",
|
||||||
|
// _for_testing
|
||||||
|
"node.title.VAEDecodeTiled": "VAE Decode (Tiled)",
|
||||||
|
"node.title.VAEEncodeTiled": "VAE Encode (Tiled)",
|
||||||
|
|
||||||
|
"node.input.SaveImage.filename_prefix": "文件名前缀",
|
||||||
|
"node.input.SaveImage.images": "图片",
|
||||||
|
|
||||||
|
"node.output.CheckpointLoaderSimple.MODEL": "模型",
|
||||||
|
|
||||||
|
"category.conditioning": "可调参数",
|
||||||
|
"category.loaders": "加载器",
|
||||||
|
"category.latent": "潜在",
|
||||||
|
"category.latent/inpaint": "潜在/修复",
|
||||||
|
"category.latent/batch": "潜在/批量",
|
||||||
|
"category.image": "图像",
|
||||||
|
"category.mask": "遮罩",
|
||||||
|
"category.image/upscaling": "图像/外扩",
|
||||||
|
"category.sampling": "采样",
|
||||||
|
"category._for_testing": "测试",
|
||||||
|
"category.latent/transform": "潜在/转换",
|
||||||
|
"category.advanced/loaders": "高级/加载器",
|
||||||
|
"category.conditioning/style_model": "可调参数/风格模型",
|
||||||
|
"category.conditioning/gligen": "可调参数/gligen",
|
||||||
|
"category.advanced/loaders/deprecated": "高级/加载器/已弃用",
|
||||||
|
"category.advanced/conditioning": "高级/可调参数",
|
||||||
|
"category.image/postprocessing": "图像/后期处理",
|
||||||
|
"category.advanced/model_merging": "高级/模型合并",
|
||||||
|
"category.image/preprocessors": "图像/前期处理",
|
||||||
|
"category.utils": "工具",
|
||||||
|
|
||||||
|
"settings.Comfy.ConfirmClear": "清空工作流需要确认",
|
||||||
|
"settings.Comfy.PromptFilename": "Prompt for filename when saving workflow",
|
||||||
|
"settings.Comfy.PreviewFormat": "预览图格式和压缩尺寸, e.g. webp, jpeg, webp;50, etc.",
|
||||||
|
"settings.Comfy.DisableSliders": "Disable sliders.",
|
||||||
|
"settings.Comfy.DevMode": "启用开发模式",
|
||||||
|
"settings.Comfy.ColorPalette": "主题",
|
||||||
|
"settings.Comfy.EditAttention.Delta": "Ctrl+up/down precision",
|
||||||
|
"settings.Comfy.InvertMenuScrolling": "反转滚动",
|
||||||
|
"settings.Comfy.LinkRenderMode": "链接渲染模式",
|
||||||
|
"settings.Comfy.NodeSuggestions.number": "Number of nodes suggestions",
|
||||||
|
"settings.Comfy.SnapToGrid.GridSize": "单元格尺寸",
|
||||||
|
"settings.Comfy.Logging.Enabled": "记录日志",
|
||||||
|
"settings.Comfy.MenuPosition": "保存菜单位置",
|
||||||
|
"settings.Comfy.ColorPalette.export": "导出",
|
||||||
|
"settings.Comfy.ColorPalette.import": "导入",
|
||||||
|
"settings.Comfy.ColorPalette.template": "模版",
|
||||||
|
"settings.Comfy.ColorPalette.delete": "删除",
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
<link rel="stylesheet" type="text/css" href="./lib/litegraph.css" />
|
<link rel="stylesheet" type="text/css" href="./lib/litegraph.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
<link rel="stylesheet" type="text/css" href="./style.css" />
|
||||||
|
<script type="text/javascript" src="./lib/i18next.js"></script>
|
||||||
|
<script type="text/javascript" src="./lib/i18nextBrowserLanguageDetector.js"></script>
|
||||||
<script type="text/javascript" src="./lib/litegraph.core.js"></script>
|
<script type="text/javascript" src="./lib/litegraph.core.js"></script>
|
||||||
<script type="text/javascript" src="./lib/litegraph.extensions.js" defer></script>
|
<script type="text/javascript" src="./lib/litegraph.extensions.js" defer></script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
|
|||||||
2265
web/lib/i18next.js
Normal file
2265
web/lib/i18next.js
Normal file
File diff suppressed because it is too large
Load Diff
422
web/lib/i18nextBrowserLanguageDetector.js
Normal file
422
web/lib/i18nextBrowserLanguageDetector.js
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||||
|
typeof define === 'function' && define.amd ? define(factory) :
|
||||||
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.i18nextBrowserLanguageDetector = factory());
|
||||||
|
})(this, (function () { 'use strict';
|
||||||
|
|
||||||
|
function _classCallCheck(instance, Constructor) {
|
||||||
|
if (!(instance instanceof Constructor)) {
|
||||||
|
throw new TypeError("Cannot call a class as a function");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _typeof(obj) {
|
||||||
|
"@babel/helpers - typeof";
|
||||||
|
|
||||||
|
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
|
||||||
|
return typeof obj;
|
||||||
|
} : function (obj) {
|
||||||
|
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
||||||
|
}, _typeof(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _toPrimitive(input, hint) {
|
||||||
|
if (_typeof(input) !== "object" || input === null) return input;
|
||||||
|
var prim = input[Symbol.toPrimitive];
|
||||||
|
if (prim !== undefined) {
|
||||||
|
var res = prim.call(input, hint || "default");
|
||||||
|
if (_typeof(res) !== "object") return res;
|
||||||
|
throw new TypeError("@@toPrimitive must return a primitive value.");
|
||||||
|
}
|
||||||
|
return (hint === "string" ? String : Number)(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _toPropertyKey(arg) {
|
||||||
|
var key = _toPrimitive(arg, "string");
|
||||||
|
return _typeof(key) === "symbol" ? key : String(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _defineProperties(target, props) {
|
||||||
|
for (var i = 0; i < props.length; i++) {
|
||||||
|
var descriptor = props[i];
|
||||||
|
descriptor.enumerable = descriptor.enumerable || false;
|
||||||
|
descriptor.configurable = true;
|
||||||
|
if ("value" in descriptor) descriptor.writable = true;
|
||||||
|
Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function _createClass(Constructor, protoProps, staticProps) {
|
||||||
|
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
||||||
|
if (staticProps) _defineProperties(Constructor, staticProps);
|
||||||
|
Object.defineProperty(Constructor, "prototype", {
|
||||||
|
writable: false
|
||||||
|
});
|
||||||
|
return Constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
var arr = [];
|
||||||
|
var each = arr.forEach;
|
||||||
|
var slice = arr.slice;
|
||||||
|
function defaults(obj) {
|
||||||
|
each.call(slice.call(arguments, 1), function (source) {
|
||||||
|
if (source) {
|
||||||
|
for (var prop in source) {
|
||||||
|
if (obj[prop] === undefined) obj[prop] = source[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
|
||||||
|
var serializeCookie = function serializeCookie(name, val, options) {
|
||||||
|
var opt = options || {};
|
||||||
|
opt.path = opt.path || '/';
|
||||||
|
var value = encodeURIComponent(val);
|
||||||
|
var str = "".concat(name, "=").concat(value);
|
||||||
|
if (opt.maxAge > 0) {
|
||||||
|
var maxAge = opt.maxAge - 0;
|
||||||
|
if (Number.isNaN(maxAge)) throw new Error('maxAge should be a Number');
|
||||||
|
str += "; Max-Age=".concat(Math.floor(maxAge));
|
||||||
|
}
|
||||||
|
if (opt.domain) {
|
||||||
|
if (!fieldContentRegExp.test(opt.domain)) {
|
||||||
|
throw new TypeError('option domain is invalid');
|
||||||
|
}
|
||||||
|
str += "; Domain=".concat(opt.domain);
|
||||||
|
}
|
||||||
|
if (opt.path) {
|
||||||
|
if (!fieldContentRegExp.test(opt.path)) {
|
||||||
|
throw new TypeError('option path is invalid');
|
||||||
|
}
|
||||||
|
str += "; Path=".concat(opt.path);
|
||||||
|
}
|
||||||
|
if (opt.expires) {
|
||||||
|
if (typeof opt.expires.toUTCString !== 'function') {
|
||||||
|
throw new TypeError('option expires is invalid');
|
||||||
|
}
|
||||||
|
str += "; Expires=".concat(opt.expires.toUTCString());
|
||||||
|
}
|
||||||
|
if (opt.httpOnly) str += '; HttpOnly';
|
||||||
|
if (opt.secure) str += '; Secure';
|
||||||
|
if (opt.sameSite) {
|
||||||
|
var sameSite = typeof opt.sameSite === 'string' ? opt.sameSite.toLowerCase() : opt.sameSite;
|
||||||
|
switch (sameSite) {
|
||||||
|
case true:
|
||||||
|
str += '; SameSite=Strict';
|
||||||
|
break;
|
||||||
|
case 'lax':
|
||||||
|
str += '; SameSite=Lax';
|
||||||
|
break;
|
||||||
|
case 'strict':
|
||||||
|
str += '; SameSite=Strict';
|
||||||
|
break;
|
||||||
|
case 'none':
|
||||||
|
str += '; SameSite=None';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new TypeError('option sameSite is invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
var cookie = {
|
||||||
|
create: function create(name, value, minutes, domain) {
|
||||||
|
var cookieOptions = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {
|
||||||
|
path: '/',
|
||||||
|
sameSite: 'strict'
|
||||||
|
};
|
||||||
|
if (minutes) {
|
||||||
|
cookieOptions.expires = new Date();
|
||||||
|
cookieOptions.expires.setTime(cookieOptions.expires.getTime() + minutes * 60 * 1000);
|
||||||
|
}
|
||||||
|
if (domain) cookieOptions.domain = domain;
|
||||||
|
document.cookie = serializeCookie(name, encodeURIComponent(value), cookieOptions);
|
||||||
|
},
|
||||||
|
read: function read(name) {
|
||||||
|
var nameEQ = "".concat(name, "=");
|
||||||
|
var ca = document.cookie.split(';');
|
||||||
|
for (var i = 0; i < ca.length; i++) {
|
||||||
|
var c = ca[i];
|
||||||
|
while (c.charAt(0) === ' ') {
|
||||||
|
c = c.substring(1, c.length);
|
||||||
|
}
|
||||||
|
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
remove: function remove(name) {
|
||||||
|
this.create(name, '', -1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var cookie$1 = {
|
||||||
|
name: 'cookie',
|
||||||
|
lookup: function lookup(options) {
|
||||||
|
var found;
|
||||||
|
if (options.lookupCookie && typeof document !== 'undefined') {
|
||||||
|
var c = cookie.read(options.lookupCookie);
|
||||||
|
if (c) found = c;
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
},
|
||||||
|
cacheUserLanguage: function cacheUserLanguage(lng, options) {
|
||||||
|
if (options.lookupCookie && typeof document !== 'undefined') {
|
||||||
|
cookie.create(options.lookupCookie, lng, options.cookieMinutes, options.cookieDomain, options.cookieOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var querystring = {
|
||||||
|
name: 'querystring',
|
||||||
|
lookup: function lookup(options) {
|
||||||
|
var found;
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
var search = window.location.search;
|
||||||
|
if (!window.location.search && window.location.hash && window.location.hash.indexOf('?') > -1) {
|
||||||
|
search = window.location.hash.substring(window.location.hash.indexOf('?'));
|
||||||
|
}
|
||||||
|
var query = search.substring(1);
|
||||||
|
var params = query.split('&');
|
||||||
|
for (var i = 0; i < params.length; i++) {
|
||||||
|
var pos = params[i].indexOf('=');
|
||||||
|
if (pos > 0) {
|
||||||
|
var key = params[i].substring(0, pos);
|
||||||
|
if (key === options.lookupQuerystring) {
|
||||||
|
found = params[i].substring(pos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var hasLocalStorageSupport = null;
|
||||||
|
var localStorageAvailable = function localStorageAvailable() {
|
||||||
|
if (hasLocalStorageSupport !== null) return hasLocalStorageSupport;
|
||||||
|
try {
|
||||||
|
hasLocalStorageSupport = window !== 'undefined' && window.localStorage !== null;
|
||||||
|
var testKey = 'i18next.translate.boo';
|
||||||
|
window.localStorage.setItem(testKey, 'foo');
|
||||||
|
window.localStorage.removeItem(testKey);
|
||||||
|
} catch (e) {
|
||||||
|
hasLocalStorageSupport = false;
|
||||||
|
}
|
||||||
|
return hasLocalStorageSupport;
|
||||||
|
};
|
||||||
|
var localStorage = {
|
||||||
|
name: 'localStorage',
|
||||||
|
lookup: function lookup(options) {
|
||||||
|
var found;
|
||||||
|
if (options.lookupLocalStorage && localStorageAvailable()) {
|
||||||
|
var lng = window.localStorage.getItem(options.lookupLocalStorage);
|
||||||
|
if (lng) found = lng;
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
},
|
||||||
|
cacheUserLanguage: function cacheUserLanguage(lng, options) {
|
||||||
|
if (options.lookupLocalStorage && localStorageAvailable()) {
|
||||||
|
window.localStorage.setItem(options.lookupLocalStorage, lng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var hasSessionStorageSupport = null;
|
||||||
|
var sessionStorageAvailable = function sessionStorageAvailable() {
|
||||||
|
if (hasSessionStorageSupport !== null) return hasSessionStorageSupport;
|
||||||
|
try {
|
||||||
|
hasSessionStorageSupport = window !== 'undefined' && window.sessionStorage !== null;
|
||||||
|
var testKey = 'i18next.translate.boo';
|
||||||
|
window.sessionStorage.setItem(testKey, 'foo');
|
||||||
|
window.sessionStorage.removeItem(testKey);
|
||||||
|
} catch (e) {
|
||||||
|
hasSessionStorageSupport = false;
|
||||||
|
}
|
||||||
|
return hasSessionStorageSupport;
|
||||||
|
};
|
||||||
|
var sessionStorage = {
|
||||||
|
name: 'sessionStorage',
|
||||||
|
lookup: function lookup(options) {
|
||||||
|
var found;
|
||||||
|
if (options.lookupSessionStorage && sessionStorageAvailable()) {
|
||||||
|
var lng = window.sessionStorage.getItem(options.lookupSessionStorage);
|
||||||
|
if (lng) found = lng;
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
},
|
||||||
|
cacheUserLanguage: function cacheUserLanguage(lng, options) {
|
||||||
|
if (options.lookupSessionStorage && sessionStorageAvailable()) {
|
||||||
|
window.sessionStorage.setItem(options.lookupSessionStorage, lng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var navigator$1 = {
|
||||||
|
name: 'navigator',
|
||||||
|
lookup: function lookup(options) {
|
||||||
|
var found = [];
|
||||||
|
if (typeof navigator !== 'undefined') {
|
||||||
|
if (navigator.languages) {
|
||||||
|
// chrome only; not an array, so can't use .push.apply instead of iterating
|
||||||
|
for (var i = 0; i < navigator.languages.length; i++) {
|
||||||
|
found.push(navigator.languages[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (navigator.userLanguage) {
|
||||||
|
found.push(navigator.userLanguage);
|
||||||
|
}
|
||||||
|
if (navigator.language) {
|
||||||
|
found.push(navigator.language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found.length > 0 ? found : undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var htmlTag = {
|
||||||
|
name: 'htmlTag',
|
||||||
|
lookup: function lookup(options) {
|
||||||
|
var found;
|
||||||
|
var htmlTag = options.htmlTag || (typeof document !== 'undefined' ? document.documentElement : null);
|
||||||
|
if (htmlTag && typeof htmlTag.getAttribute === 'function') {
|
||||||
|
found = htmlTag.getAttribute('lang');
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var path = {
|
||||||
|
name: 'path',
|
||||||
|
lookup: function lookup(options) {
|
||||||
|
var found;
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
var language = window.location.pathname.match(/\/([a-zA-Z-]*)/g);
|
||||||
|
if (language instanceof Array) {
|
||||||
|
if (typeof options.lookupFromPathIndex === 'number') {
|
||||||
|
if (typeof language[options.lookupFromPathIndex] !== 'string') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
found = language[options.lookupFromPathIndex].replace('/', '');
|
||||||
|
} else {
|
||||||
|
found = language[0].replace('/', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var subdomain = {
|
||||||
|
name: 'subdomain',
|
||||||
|
lookup: function lookup(options) {
|
||||||
|
// If given get the subdomain index else 1
|
||||||
|
var lookupFromSubdomainIndex = typeof options.lookupFromSubdomainIndex === 'number' ? options.lookupFromSubdomainIndex + 1 : 1;
|
||||||
|
// get all matches if window.location. is existing
|
||||||
|
// first item of match is the match itself and the second is the first group macht which sould be the first subdomain match
|
||||||
|
// is the hostname no public domain get the or option of localhost
|
||||||
|
var language = typeof window !== 'undefined' && window.location && window.location.hostname && window.location.hostname.match(/^(\w{2,5})\.(([a-z0-9-]{1,63}\.[a-z]{2,6})|localhost)/i);
|
||||||
|
|
||||||
|
// if there is no match (null) return undefined
|
||||||
|
if (!language) return undefined;
|
||||||
|
// return the given group match
|
||||||
|
return language[lookupFromSubdomainIndex];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getDefaults() {
|
||||||
|
return {
|
||||||
|
order: ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag'],
|
||||||
|
lookupQuerystring: 'lng',
|
||||||
|
lookupCookie: 'i18next',
|
||||||
|
lookupLocalStorage: 'i18nextLng',
|
||||||
|
lookupSessionStorage: 'i18nextLng',
|
||||||
|
// cache user language
|
||||||
|
caches: ['localStorage'],
|
||||||
|
excludeCacheFor: ['cimode'],
|
||||||
|
// cookieMinutes: 10,
|
||||||
|
// cookieDomain: 'myDomain'
|
||||||
|
|
||||||
|
convertDetectedLanguage: function convertDetectedLanguage(l) {
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var Browser = /*#__PURE__*/function () {
|
||||||
|
function Browser(services) {
|
||||||
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||||
|
_classCallCheck(this, Browser);
|
||||||
|
this.type = 'languageDetector';
|
||||||
|
this.detectors = {};
|
||||||
|
this.init(services, options);
|
||||||
|
}
|
||||||
|
_createClass(Browser, [{
|
||||||
|
key: "init",
|
||||||
|
value: function init(services) {
|
||||||
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||||
|
var i18nOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
||||||
|
this.services = services || {
|
||||||
|
languageUtils: {}
|
||||||
|
}; // this way the language detector can be used without i18next
|
||||||
|
this.options = defaults(options, this.options || {}, getDefaults());
|
||||||
|
if (typeof this.options.convertDetectedLanguage === 'string' && this.options.convertDetectedLanguage.indexOf('15897') > -1) {
|
||||||
|
this.options.convertDetectedLanguage = function (l) {
|
||||||
|
return l.replace('-', '_');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// backwards compatibility
|
||||||
|
if (this.options.lookupFromUrlIndex) this.options.lookupFromPathIndex = this.options.lookupFromUrlIndex;
|
||||||
|
this.i18nOptions = i18nOptions;
|
||||||
|
this.addDetector(cookie$1);
|
||||||
|
this.addDetector(querystring);
|
||||||
|
this.addDetector(localStorage);
|
||||||
|
this.addDetector(sessionStorage);
|
||||||
|
this.addDetector(navigator$1);
|
||||||
|
this.addDetector(htmlTag);
|
||||||
|
this.addDetector(path);
|
||||||
|
this.addDetector(subdomain);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: "addDetector",
|
||||||
|
value: function addDetector(detector) {
|
||||||
|
this.detectors[detector.name] = detector;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: "detect",
|
||||||
|
value: function detect(detectionOrder) {
|
||||||
|
var _this = this;
|
||||||
|
if (!detectionOrder) detectionOrder = this.options.order;
|
||||||
|
var detected = [];
|
||||||
|
detectionOrder.forEach(function (detectorName) {
|
||||||
|
if (_this.detectors[detectorName]) {
|
||||||
|
var lookup = _this.detectors[detectorName].lookup(_this.options);
|
||||||
|
if (lookup && typeof lookup === 'string') lookup = [lookup];
|
||||||
|
if (lookup) detected = detected.concat(lookup);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
detected = detected.map(function (d) {
|
||||||
|
return _this.options.convertDetectedLanguage(d);
|
||||||
|
});
|
||||||
|
if (this.services.languageUtils.getBestMatchFromCodes) return detected; // new i18next v19.5.0
|
||||||
|
return detected.length > 0 ? detected[0] : null; // a little backward compatibility
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: "cacheUserLanguage",
|
||||||
|
value: function cacheUserLanguage(lng, caches) {
|
||||||
|
var _this2 = this;
|
||||||
|
if (!caches) caches = this.options.caches;
|
||||||
|
if (!caches) return;
|
||||||
|
if (this.options.excludeCacheFor && this.options.excludeCacheFor.indexOf(lng) > -1) return;
|
||||||
|
caches.forEach(function (cacheName) {
|
||||||
|
if (_this2.detectors[cacheName]) _this2.detectors[cacheName].cacheUserLanguage(lng, _this2.options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
return Browser;
|
||||||
|
}();
|
||||||
|
Browser.type = 'languageDetector';
|
||||||
|
|
||||||
|
return Browser;
|
||||||
|
|
||||||
|
}));
|
||||||
@ -9835,7 +9835,11 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
ctx.textAlign = "center";
|
ctx.textAlign = "center";
|
||||||
ctx.fillStyle = text_color;
|
ctx.fillStyle = text_color;
|
||||||
ctx.fillText(
|
ctx.fillText(
|
||||||
w.label || w.name + " " + Number(w.value).toFixed(3),
|
w.label || w.name + " " + Number(w.value).toFixed(
|
||||||
|
w.options.precision != null
|
||||||
|
? w.options.precision
|
||||||
|
: 3
|
||||||
|
),
|
||||||
widget_width * 0.5,
|
widget_width * 0.5,
|
||||||
y + H * 0.7
|
y + H * 0.7
|
||||||
);
|
);
|
||||||
@ -12518,7 +12522,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
panel.content.innerHTML = ""; //clear
|
panel.content.innerHTML = ""; //clear
|
||||||
panel.addHTML("<span class='node_type'>"+node.type+"</span><span class='node_desc'>"+(node.constructor.desc || "")+"</span><span class='separator'></span>");
|
panel.addHTML("<span class='node_type'>"+node.type+"</span><span class='node_desc'>"+(node.constructor.desc || "")+"</span><span class='separator'></span>");
|
||||||
|
|
||||||
panel.addHTML("<h3>Properties</h3>");
|
panel.addHTML(`<h3>${i18next.t("ui.node_panel.header.properties")}</h3>`);
|
||||||
|
|
||||||
var fUpdate = function(name,value){
|
var fUpdate = function(name,value){
|
||||||
graphcanvas.graph.beforeChange(node);
|
graphcanvas.graph.beforeChange(node);
|
||||||
@ -12550,16 +12554,16 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
graphcanvas.dirty_canvas = true;
|
graphcanvas.dirty_canvas = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
panel.addWidget( "string", "Title", node.title, {}, fUpdate);
|
panel.addWidget( "string", i18next.t("ui.node_panel.header.title"), node.title, {}, fUpdate);
|
||||||
|
|
||||||
panel.addWidget( "combo", "Mode", LiteGraph.NODE_MODES[node.mode], {values: LiteGraph.NODE_MODES}, fUpdate);
|
panel.addWidget( "combo", i18next.t("ui.node_panel.header.mode"), LiteGraph.NODE_MODES[node.mode], {values: LiteGraph.NODE_MODES}, fUpdate);
|
||||||
|
|
||||||
var nodeCol = "";
|
var nodeCol = "";
|
||||||
if (node.color !== undefined){
|
if (node.color !== undefined){
|
||||||
nodeCol = Object.keys(LGraphCanvas.node_colors).filter(function(nK){ return LGraphCanvas.node_colors[nK].color == node.color; });
|
nodeCol = Object.keys(LGraphCanvas.node_colors).filter(function(nK){ return LGraphCanvas.node_colors[nK].color == node.color; });
|
||||||
}
|
}
|
||||||
|
|
||||||
panel.addWidget( "combo", "Color", nodeCol, {values: Object.keys(LGraphCanvas.node_colors)}, fUpdate);
|
panel.addWidget( "combo", i18next.t("ui.node_panel.header.color"), nodeCol, {values: Object.keys(LGraphCanvas.node_colors)}, fUpdate);
|
||||||
|
|
||||||
for(var pName in node.properties)
|
for(var pName in node.properties)
|
||||||
{
|
{
|
||||||
@ -13835,7 +13839,7 @@ LGraphNode.prototype.executeAction = function(action)
|
|||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
element.addEventListener("click", inner_onclick);
|
element.addEventListener("click", inner_onclick);
|
||||||
}
|
}
|
||||||
if (options.autoopen) {
|
if (!disabled && options.autoopen) {
|
||||||
LiteGraph.pointerListenerAdd(element,"enter",inner_over);
|
LiteGraph.pointerListenerAdd(element,"enter",inner_over);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -264,6 +264,15 @@ class ComfyApi extends EventTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets system & device stats
|
||||||
|
* @returns System stats such as python version, OS, per device info
|
||||||
|
*/
|
||||||
|
async getSystemStats() {
|
||||||
|
const res = await this.fetchApi("/system_stats");
|
||||||
|
return await res.json();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a POST request to the API
|
* Sends a POST request to the API
|
||||||
* @param {*} type The endpoint to post to
|
* @param {*} type The endpoint to post to
|
||||||
|
|||||||
@ -1,9 +1,23 @@
|
|||||||
|
import { ComfyLogging } from "./logging.js";
|
||||||
import { ComfyWidgets } from "./widgets.js";
|
import { ComfyWidgets } from "./widgets.js";
|
||||||
import { ComfyUI, $el } from "./ui.js";
|
import { ComfyUI, $el } from "./ui.js";
|
||||||
import { api } from "./api.js";
|
import { api } from "./api.js";
|
||||||
import { defaultGraph } from "./defaultGraph.js";
|
import { defaultGraph } from "./defaultGraph.js";
|
||||||
import { getPngMetadata, importA1111, getLatentMetadata } from "./pnginfo.js";
|
import { getPngMetadata, importA1111, getLatentMetadata } from "./pnginfo.js";
|
||||||
|
|
||||||
|
import en from "../i18n/en_US.js"
|
||||||
|
import cn from "../i18n/zh_CN.js"
|
||||||
|
|
||||||
|
i18next.use(i18nextBrowserLanguageDetector).init({
|
||||||
|
fallbackLng: 'en',
|
||||||
|
resources: {
|
||||||
|
"en-US": en,
|
||||||
|
"en": en,
|
||||||
|
"zh_CN": cn,
|
||||||
|
"cn": cn,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import("types/comfy").ComfyExtension} ComfyExtension
|
* @typedef {import("types/comfy").ComfyExtension} ComfyExtension
|
||||||
*/
|
*/
|
||||||
@ -31,6 +45,7 @@ export class ComfyApp {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.ui = new ComfyUI(this);
|
this.ui = new ComfyUI(this);
|
||||||
|
this.logging = new ComfyLogging(this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of extensions that are registered with the app
|
* List of extensions that are registered with the app
|
||||||
@ -59,7 +74,7 @@ export class ComfyApp {
|
|||||||
|
|
||||||
getPreviewFormatParam() {
|
getPreviewFormatParam() {
|
||||||
let preview_format = this.ui.settings.getSettingValue("Comfy.PreviewFormat");
|
let preview_format = this.ui.settings.getSettingValue("Comfy.PreviewFormat");
|
||||||
if(preview_format)
|
if (preview_format)
|
||||||
return `&preview=${preview_format}`;
|
return `&preview=${preview_format}`;
|
||||||
else
|
else
|
||||||
return "";
|
return "";
|
||||||
@ -70,7 +85,7 @@ export class ComfyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static onClipspaceEditorSave() {
|
static onClipspaceEditorSave() {
|
||||||
if(ComfyApp.clipspace_return_node) {
|
if (ComfyApp.clipspace_return_node) {
|
||||||
ComfyApp.pasteFromClipspace(ComfyApp.clipspace_return_node);
|
ComfyApp.pasteFromClipspace(ComfyApp.clipspace_return_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,13 +96,13 @@ export class ComfyApp {
|
|||||||
|
|
||||||
static copyToClipspace(node) {
|
static copyToClipspace(node) {
|
||||||
var widgets = null;
|
var widgets = null;
|
||||||
if(node.widgets) {
|
if (node.widgets) {
|
||||||
widgets = node.widgets.map(({ type, name, value }) => ({ type, name, value }));
|
widgets = node.widgets.map(({ type, name, value }) => ({ type, name, value }));
|
||||||
}
|
}
|
||||||
|
|
||||||
var imgs = undefined;
|
var imgs = undefined;
|
||||||
var orig_imgs = undefined;
|
var orig_imgs = undefined;
|
||||||
if(node.imgs != undefined) {
|
if (node.imgs != undefined) {
|
||||||
imgs = [];
|
imgs = [];
|
||||||
orig_imgs = [];
|
orig_imgs = [];
|
||||||
|
|
||||||
@ -99,7 +114,7 @@ export class ComfyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var selectedIndex = 0;
|
var selectedIndex = 0;
|
||||||
if(node.imageIndex) {
|
if (node.imageIndex) {
|
||||||
selectedIndex = node.imageIndex;
|
selectedIndex = node.imageIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,30 +129,30 @@ export class ComfyApp {
|
|||||||
|
|
||||||
ComfyApp.clipspace_return_node = null;
|
ComfyApp.clipspace_return_node = null;
|
||||||
|
|
||||||
if(ComfyApp.clipspace_invalidate_handler) {
|
if (ComfyApp.clipspace_invalidate_handler) {
|
||||||
ComfyApp.clipspace_invalidate_handler();
|
ComfyApp.clipspace_invalidate_handler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static pasteFromClipspace(node) {
|
static pasteFromClipspace(node) {
|
||||||
if(ComfyApp.clipspace) {
|
if (ComfyApp.clipspace) {
|
||||||
// image paste
|
// image paste
|
||||||
if(ComfyApp.clipspace.imgs && node.imgs) {
|
if (ComfyApp.clipspace.imgs && node.imgs) {
|
||||||
if(node.images && ComfyApp.clipspace.images) {
|
if (node.images && ComfyApp.clipspace.images) {
|
||||||
if(ComfyApp.clipspace['img_paste_mode'] == 'selected') {
|
if (ComfyApp.clipspace['img_paste_mode'] == 'selected') {
|
||||||
node.images = [ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']]];
|
node.images = [ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']]];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
node.images = ComfyApp.clipspace.images;
|
node.images = ComfyApp.clipspace.images;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(app.nodeOutputs[node.id + ""])
|
if (app.nodeOutputs[node.id + ""])
|
||||||
app.nodeOutputs[node.id + ""].images = node.images;
|
app.nodeOutputs[node.id + ""].images = node.images;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ComfyApp.clipspace.imgs) {
|
if (ComfyApp.clipspace.imgs) {
|
||||||
// deep-copy to cut link with clipspace
|
// deep-copy to cut link with clipspace
|
||||||
if(ComfyApp.clipspace['img_paste_mode'] == 'selected') {
|
if (ComfyApp.clipspace['img_paste_mode'] == 'selected') {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.src = ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src;
|
img.src = ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src;
|
||||||
node.imgs = [img];
|
node.imgs = [img];
|
||||||
@ -145,7 +160,7 @@ export class ComfyApp {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const imgs = [];
|
const imgs = [];
|
||||||
for(let i=0; i<ComfyApp.clipspace.imgs.length; i++) {
|
for (let i = 0; i < ComfyApp.clipspace.imgs.length; i++) {
|
||||||
imgs[i] = new Image();
|
imgs[i] = new Image();
|
||||||
imgs[i].src = ComfyApp.clipspace.imgs[i].src;
|
imgs[i].src = ComfyApp.clipspace.imgs[i].src;
|
||||||
node.imgs = imgs;
|
node.imgs = imgs;
|
||||||
@ -154,25 +169,25 @@ export class ComfyApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(node.widgets) {
|
if (node.widgets) {
|
||||||
if(ComfyApp.clipspace.images) {
|
if (ComfyApp.clipspace.images) {
|
||||||
const clip_image = ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']];
|
const clip_image = ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']];
|
||||||
const index = node.widgets.findIndex(obj => obj.name === 'image');
|
const index = node.widgets.findIndex(obj => obj.name === 'image');
|
||||||
if(index >= 0) {
|
if (index >= 0) {
|
||||||
if(node.widgets[index].type != 'image' && typeof node.widgets[index].value == "string" && clip_image.filename) {
|
if (node.widgets[index].type != 'image' && typeof node.widgets[index].value == "string" && clip_image.filename) {
|
||||||
node.widgets[index].value = (clip_image.subfolder?clip_image.subfolder+'/':'') + clip_image.filename + (clip_image.type?` [${clip_image.type}]`:'');
|
node.widgets[index].value = (clip_image.subfolder ? clip_image.subfolder + '/' : '') + clip_image.filename + (clip_image.type ? ` [${clip_image.type}]` : '');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
node.widgets[index].value = clip_image;
|
node.widgets[index].value = clip_image;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(ComfyApp.clipspace.widgets) {
|
if (ComfyApp.clipspace.widgets) {
|
||||||
ComfyApp.clipspace.widgets.forEach(({ type, name, value }) => {
|
ComfyApp.clipspace.widgets.forEach(({ type, name, value }) => {
|
||||||
const prop = Object.values(node.widgets).find(obj => obj.type === type && obj.name === name);
|
const prop = Object.values(node.widgets).find(obj => obj.type === type && obj.name === name);
|
||||||
if (prop && prop.type != 'button') {
|
if (prop && prop.type != 'button') {
|
||||||
if(prop.type != 'image' && typeof prop.value == "string" && value.filename) {
|
if (prop.type != 'image' && typeof prop.value == "string" && value.filename) {
|
||||||
prop.value = (value.subfolder?value.subfolder+'/':'') + value.filename + (value.type?` [${value.type}]`:'');
|
prop.value = (value.subfolder ? value.subfolder + '/' : '') + value.filename + (value.type ? ` [${value.type}]` : '');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
prop.value = value;
|
prop.value = value;
|
||||||
@ -283,28 +298,28 @@ export class ComfyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prevent conflict of clipspace content
|
// prevent conflict of clipspace content
|
||||||
if(!ComfyApp.clipspace_return_node) {
|
if (!ComfyApp.clipspace_return_node) {
|
||||||
options.push({
|
options.push({
|
||||||
content: "Copy (Clipspace)",
|
content: "Copy (Clipspace)",
|
||||||
callback: (obj) => { ComfyApp.copyToClipspace(this); }
|
callback: (obj) => { ComfyApp.copyToClipspace(this); }
|
||||||
});
|
});
|
||||||
|
|
||||||
if(ComfyApp.clipspace != null) {
|
if (ComfyApp.clipspace != null) {
|
||||||
options.push({
|
options.push({
|
||||||
content: "Paste (Clipspace)",
|
content: "Paste (Clipspace)",
|
||||||
callback: () => { ComfyApp.pasteFromClipspace(this); }
|
callback: () => { ComfyApp.pasteFromClipspace(this); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ComfyApp.isImageNode(this)) {
|
if (ComfyApp.isImageNode(this)) {
|
||||||
options.push({
|
options.push({
|
||||||
content: "Open in MaskEditor",
|
content: "Open in MaskEditor",
|
||||||
callback: (obj) => {
|
callback: (obj) => {
|
||||||
ComfyApp.copyToClipspace(this);
|
ComfyApp.copyToClipspace(this);
|
||||||
ComfyApp.clipspace_return_node = this;
|
ComfyApp.clipspace_return_node = this;
|
||||||
ComfyApp.open_maskeditor();
|
ComfyApp.open_maskeditor();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -314,7 +329,7 @@ export class ComfyApp {
|
|||||||
const app = this;
|
const app = this;
|
||||||
const origNodeOnKeyDown = node.prototype.onKeyDown;
|
const origNodeOnKeyDown = node.prototype.onKeyDown;
|
||||||
|
|
||||||
node.prototype.onKeyDown = function(e) {
|
node.prototype.onKeyDown = function (e) {
|
||||||
if (origNodeOnKeyDown && origNodeOnKeyDown.apply(this, e) === false) {
|
if (origNodeOnKeyDown && origNodeOnKeyDown.apply(this, e) === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -369,7 +384,7 @@ export class ComfyApp {
|
|||||||
if (w.computeSize) {
|
if (w.computeSize) {
|
||||||
shiftY += w.computeSize()[1] + 4;
|
shiftY += w.computeSize()[1] + 4;
|
||||||
}
|
}
|
||||||
else if(w.computedHeight) {
|
else if (w.computedHeight) {
|
||||||
shiftY += w.computedHeight;
|
shiftY += w.computedHeight;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -614,7 +629,7 @@ export class ComfyApp {
|
|||||||
}
|
}
|
||||||
// Dragging from Chrome->Firefox there is a file but its a bmp, so ignore that
|
// Dragging from Chrome->Firefox there is a file but its a bmp, so ignore that
|
||||||
if (event.dataTransfer.files.length && event.dataTransfer.files[0].type !== "image/bmp") {
|
if (event.dataTransfer.files.length && event.dataTransfer.files[0].type !== "image/bmp") {
|
||||||
await this.handleFile(event.dataTransfer.files[0]);
|
await this.handleFile(event.dataTransfer.files[0]);
|
||||||
} else {
|
} else {
|
||||||
// Try loading the first URI in the transfer list
|
// Try loading the first URI in the transfer list
|
||||||
const validTypes = ["text/uri-list", "text/x-moz-url"];
|
const validTypes = ["text/uri-list", "text/x-moz-url"];
|
||||||
@ -674,7 +689,7 @@ export class ComfyApp {
|
|||||||
data = data.slice(data.indexOf("workflow\n"));
|
data = data.slice(data.indexOf("workflow\n"));
|
||||||
data = data.slice(data.indexOf("{"));
|
data = data.slice(data.indexOf("{"));
|
||||||
workflow = JSON.parse(data);
|
workflow = JSON.parse(data);
|
||||||
} catch (error) {}
|
} catch (error) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workflow && workflow.version && workflow.nodes && workflow.extra) {
|
if (workflow && workflow.version && workflow.nodes && workflow.extra) {
|
||||||
@ -692,7 +707,7 @@ export class ComfyApp {
|
|||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
const origProcessMouseDown = LGraphCanvas.prototype.processMouseDown;
|
const origProcessMouseDown = LGraphCanvas.prototype.processMouseDown;
|
||||||
LGraphCanvas.prototype.processMouseDown = function(e) {
|
LGraphCanvas.prototype.processMouseDown = function (e) {
|
||||||
const res = origProcessMouseDown.apply(this, arguments);
|
const res = origProcessMouseDown.apply(this, arguments);
|
||||||
|
|
||||||
this.selected_group_moving = false;
|
this.selected_group_moving = false;
|
||||||
@ -712,7 +727,7 @@ export class ComfyApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const origProcessMouseMove = LGraphCanvas.prototype.processMouseMove;
|
const origProcessMouseMove = LGraphCanvas.prototype.processMouseMove;
|
||||||
LGraphCanvas.prototype.processMouseMove = function(e) {
|
LGraphCanvas.prototype.processMouseMove = function (e) {
|
||||||
const orig_selected_group = this.selected_group;
|
const orig_selected_group = this.selected_group;
|
||||||
|
|
||||||
if (this.selected_group && !this.selected_group_resizing && !this.selected_group_moving) {
|
if (this.selected_group && !this.selected_group_resizing && !this.selected_group_moving) {
|
||||||
@ -737,7 +752,7 @@ export class ComfyApp {
|
|||||||
#addProcessKeyHandler() {
|
#addProcessKeyHandler() {
|
||||||
const self = this;
|
const self = this;
|
||||||
const origProcessKey = LGraphCanvas.prototype.processKey;
|
const origProcessKey = LGraphCanvas.prototype.processKey;
|
||||||
LGraphCanvas.prototype.processKey = function(e) {
|
LGraphCanvas.prototype.processKey = function (e) {
|
||||||
const res = origProcessKey.apply(this, arguments);
|
const res = origProcessKey.apply(this, arguments);
|
||||||
|
|
||||||
if (res === false) {
|
if (res === false) {
|
||||||
@ -802,7 +817,7 @@ export class ComfyApp {
|
|||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
const origDrawGroups = LGraphCanvas.prototype.drawGroups;
|
const origDrawGroups = LGraphCanvas.prototype.drawGroups;
|
||||||
LGraphCanvas.prototype.drawGroups = function(canvas, ctx) {
|
LGraphCanvas.prototype.drawGroups = function (canvas, ctx) {
|
||||||
if (!this.graph) {
|
if (!this.graph) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -889,7 +904,7 @@ export class ComfyApp {
|
|||||||
12 + size[0] + 1,
|
12 + size[0] + 1,
|
||||||
12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT,
|
12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT,
|
||||||
[this.round_radius * 2, this.round_radius * 2, 2, 2]
|
[this.round_radius * 2, this.round_radius * 2, 2, 2]
|
||||||
);
|
);
|
||||||
else if (shape == LiteGraph.CIRCLE_SHAPE)
|
else if (shape == LiteGraph.CIRCLE_SHAPE)
|
||||||
ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5 + 6, 0, Math.PI * 2);
|
ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5 + 6, 0, Math.PI * 2);
|
||||||
ctx.strokeStyle = color;
|
ctx.strokeStyle = color;
|
||||||
@ -1023,6 +1038,7 @@ export class ComfyApp {
|
|||||||
*/
|
*/
|
||||||
async #loadExtensions() {
|
async #loadExtensions() {
|
||||||
const extensions = await api.getExtensions();
|
const extensions = await api.getExtensions();
|
||||||
|
this.logging.addEntry("Comfy.App", "debug", { Extensions: extensions });
|
||||||
for (const ext of extensions) {
|
for (const ext of extensions) {
|
||||||
try {
|
try {
|
||||||
await import(api.apiURL(ext));
|
await import(api.apiURL(ext));
|
||||||
@ -1114,9 +1130,10 @@ export class ComfyApp {
|
|||||||
const defs = await api.getNodeDefs();
|
const defs = await api.getNodeDefs();
|
||||||
await this.registerNodesFromDefs(defs);
|
await this.registerNodesFromDefs(defs);
|
||||||
await this.#invokeExtensionsAsync("registerCustomNodes");
|
await this.#invokeExtensionsAsync("registerCustomNodes");
|
||||||
|
await this.#invokeExtensionsAsync("afterNodesRegistrations")
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerNodesFromDefs(defs) {
|
async registerNodesFromDefs(defs) {
|
||||||
await this.#invokeExtensionsAsync("addCustomNodeDefs", defs);
|
await this.#invokeExtensionsAsync("addCustomNodeDefs", defs);
|
||||||
|
|
||||||
// Generate list of known widgets
|
// Generate list of known widgets
|
||||||
@ -1132,15 +1149,15 @@ export class ComfyApp {
|
|||||||
const node = Object.assign(
|
const node = Object.assign(
|
||||||
function ComfyNode() {
|
function ComfyNode() {
|
||||||
var inputs = nodeData["input"]["required"];
|
var inputs = nodeData["input"]["required"];
|
||||||
if (nodeData["input"]["optional"] != undefined){
|
if (nodeData["input"]["optional"] != undefined) {
|
||||||
inputs = Object.assign({}, nodeData["input"]["required"], nodeData["input"]["optional"])
|
inputs = Object.assign({}, nodeData["input"]["required"], nodeData["input"]["optional"])
|
||||||
}
|
}
|
||||||
const config = { minWidth: 1, minHeight: 1 };
|
const config = { minWidth: 1, minHeight: 1 };
|
||||||
for (const inputName in inputs) {
|
for (const inputName in inputs) {
|
||||||
const inputData = inputs[inputName];
|
const inputData = inputs[inputName];
|
||||||
const type = inputData[0];
|
const type = inputData[0];
|
||||||
|
|
||||||
if(inputData[1]?.forceInput) {
|
if (inputData[1]?.forceInput) {
|
||||||
this.addInput(inputName, type);
|
this.addInput(inputName, type);
|
||||||
} else {
|
} else {
|
||||||
if (Array.isArray(type)) {
|
if (Array.isArray(type)) {
|
||||||
@ -1162,7 +1179,7 @@ export class ComfyApp {
|
|||||||
for (const o in nodeData["output"]) {
|
for (const o in nodeData["output"]) {
|
||||||
const output = nodeData["output"][o];
|
const output = nodeData["output"][o];
|
||||||
const outputName = nodeData["output_name"][o] || output;
|
const outputName = nodeData["output_name"][o] || output;
|
||||||
const outputShape = nodeData["output_is_list"][o] ? LiteGraph.GRID_SHAPE : LiteGraph.CIRCLE_SHAPE ;
|
const outputShape = nodeData["output_is_list"][o] ? LiteGraph.GRID_SHAPE : LiteGraph.CIRCLE_SHAPE;
|
||||||
this.addOutput(outputName, output, { shape: outputShape });
|
this.addOutput(outputName, output, { shape: outputShape });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1306,6 +1323,9 @@ export class ComfyApp {
|
|||||||
(t) => `<li>${t}</li>`
|
(t) => `<li>${t}</li>`
|
||||||
).join("")}</ul>Nodes that have failed to load will show as red on the graph.`
|
).join("")}</ul>Nodes that have failed to load will show as red on the graph.`
|
||||||
);
|
);
|
||||||
|
this.logging.addEntry("Comfy.App", "warn", {
|
||||||
|
MissingNodes: missingNodeTypes,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1356,7 +1376,7 @@ export class ComfyApp {
|
|||||||
if (parent.isVirtualNode) {
|
if (parent.isVirtualNode) {
|
||||||
link = parent.getInputLink(link.origin_slot);
|
link = parent.getInputLink(link.origin_slot);
|
||||||
if (link) {
|
if (link) {
|
||||||
parent = parent.getInputNode(link.origin_slot);
|
parent = parent.getInputNode(link.target_slot);
|
||||||
if (parent) {
|
if (parent) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
@ -1424,9 +1444,9 @@ export class ComfyApp {
|
|||||||
else if (error.response) {
|
else if (error.response) {
|
||||||
let message = error.response.error.message;
|
let message = error.response.error.message;
|
||||||
if (error.response.error.details)
|
if (error.response.error.details)
|
||||||
message += ": " + error.response.error.details;
|
message += ": " + error.response.error.details;
|
||||||
for (const [nodeID, nodeError] of Object.entries(error.response.node_errors)) {
|
for (const [nodeID, nodeError] of Object.entries(error.response.node_errors)) {
|
||||||
message += "\n" + nodeError.class_type + ":"
|
message += "\n" + nodeError.class_type + ":"
|
||||||
for (const errorReason of nodeError.errors) {
|
for (const errorReason of nodeError.errors) {
|
||||||
message += "\n - " + errorReason.message + ": " + errorReason.details
|
message += "\n - " + errorReason.message + ": " + errorReason.details
|
||||||
}
|
}
|
||||||
@ -1552,22 +1572,22 @@ export class ComfyApp {
|
|||||||
async refreshComboInNodes() {
|
async refreshComboInNodes() {
|
||||||
const defs = await api.getNodeDefs();
|
const defs = await api.getNodeDefs();
|
||||||
|
|
||||||
for(let nodeNum in this.graph._nodes) {
|
for (let nodeNum in this.graph._nodes) {
|
||||||
const node = this.graph._nodes[nodeNum];
|
const node = this.graph._nodes[nodeNum];
|
||||||
|
|
||||||
const def = defs[node.type];
|
const def = defs[node.type];
|
||||||
|
|
||||||
// HOTFIX: The current patch is designed to prevent the rest of the code from breaking due to primitive nodes,
|
// HOTFIX: The current patch is designed to prevent the rest of the code from breaking due to primitive nodes,
|
||||||
// and additional work is needed to consider the primitive logic in the refresh logic.
|
// and additional work is needed to consider the primitive logic in the refresh logic.
|
||||||
if(!def)
|
if (!def)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for(const widgetNum in node.widgets) {
|
for (const widgetNum in node.widgets) {
|
||||||
const widget = node.widgets[widgetNum]
|
const widget = node.widgets[widgetNum]
|
||||||
if(widget.type == "combo" && def["input"]["required"][widget.name] !== undefined) {
|
if (widget.type == "combo" && def["input"]["required"][widget.name] !== undefined) {
|
||||||
widget.options.values = def["input"]["required"][widget.name][0];
|
widget.options.values = def["input"]["required"][widget.name][0];
|
||||||
|
|
||||||
if(widget.name != 'image' && !widget.options.values.includes(widget.value)) {
|
if (widget.name != 'image' && !widget.options.values.includes(widget.value)) {
|
||||||
widget.value = widget.options.values[0];
|
widget.value = widget.options.values[0];
|
||||||
widget.callback(widget.value);
|
widget.callback(widget.value);
|
||||||
}
|
}
|
||||||
|
|||||||
367
web/scripts/logging.js
Normal file
367
web/scripts/logging.js
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
import { $el, ComfyDialog } from "./ui.js";
|
||||||
|
import { api } from "./api.js";
|
||||||
|
|
||||||
|
$el("style", {
|
||||||
|
textContent: `
|
||||||
|
.comfy-logging-logs {
|
||||||
|
display: grid;
|
||||||
|
color: var(--fg-color);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
.comfy-logging-log {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
.comfy-logging-title {
|
||||||
|
background: var(--tr-even-bg-color);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.comfy-logging-log div {
|
||||||
|
background: var(--row-bg);
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
parent: document.body,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stringify function supporting max depth and removal of circular references
|
||||||
|
// https://stackoverflow.com/a/57193345
|
||||||
|
function stringify(val, depth, replacer, space, onGetObjID) {
|
||||||
|
depth = isNaN(+depth) ? 1 : depth;
|
||||||
|
var recursMap = new WeakMap();
|
||||||
|
function _build(val, depth, o, a, r) {
|
||||||
|
// (JSON.stringify() has it's own rules, which we respect here by using it for property iteration)
|
||||||
|
return !val || typeof val != "object"
|
||||||
|
? val
|
||||||
|
: ((r = recursMap.has(val)),
|
||||||
|
recursMap.set(val, true),
|
||||||
|
(a = Array.isArray(val)),
|
||||||
|
r
|
||||||
|
? (o = (onGetObjID && onGetObjID(val)) || null)
|
||||||
|
: JSON.stringify(val, function (k, v) {
|
||||||
|
if (a || depth > 0) {
|
||||||
|
if (replacer) v = replacer(k, v);
|
||||||
|
if (!k) return (a = Array.isArray(v)), (val = v);
|
||||||
|
!o && (o = a ? [] : {});
|
||||||
|
o[k] = _build(v, a ? depth : depth - 1);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
o === void 0 ? (a ? [] : {}) : o);
|
||||||
|
}
|
||||||
|
return JSON.stringify(_build(val, depth), null, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsonReplacer = (k, v, ui) => {
|
||||||
|
if (v instanceof Array && v.length === 1) {
|
||||||
|
v = v[0];
|
||||||
|
}
|
||||||
|
if (v instanceof Date) {
|
||||||
|
v = v.toISOString();
|
||||||
|
if (ui) {
|
||||||
|
v = v.split("T")[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (v instanceof Error) {
|
||||||
|
let err = "";
|
||||||
|
if (v.name) err += v.name + "\n";
|
||||||
|
if (v.message) err += v.message + "\n";
|
||||||
|
if (v.stack) err += v.stack + "\n";
|
||||||
|
if (!err) {
|
||||||
|
err = v.toString();
|
||||||
|
}
|
||||||
|
v = err;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileInput = $el("input", {
|
||||||
|
type: "file",
|
||||||
|
accept: ".json",
|
||||||
|
style: { display: "none" },
|
||||||
|
parent: document.body,
|
||||||
|
});
|
||||||
|
|
||||||
|
class ComfyLoggingDialog extends ComfyDialog {
|
||||||
|
constructor(logging) {
|
||||||
|
super();
|
||||||
|
this.logging = logging;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.logging.clear();
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
export() {
|
||||||
|
const blob = new Blob([stringify([...this.logging.entries], 20, jsonReplacer, "\t")], {
|
||||||
|
type: "application/json",
|
||||||
|
});
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = $el("a", {
|
||||||
|
href: url,
|
||||||
|
download: `comfyui-logs-${Date.now()}.json`,
|
||||||
|
style: { display: "none" },
|
||||||
|
parent: document.body,
|
||||||
|
});
|
||||||
|
a.click();
|
||||||
|
setTimeout(function () {
|
||||||
|
a.remove();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
import() {
|
||||||
|
fileInput.onchange = () => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
fileInput.remove();
|
||||||
|
try {
|
||||||
|
const obj = JSON.parse(reader.result);
|
||||||
|
if (obj instanceof Array) {
|
||||||
|
this.show(obj);
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid file selected.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert("Unable to load logs: " + error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(fileInput.files[0]);
|
||||||
|
};
|
||||||
|
fileInput.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
createButtons() {
|
||||||
|
return [
|
||||||
|
$el("button", {
|
||||||
|
type: "button",
|
||||||
|
textContent: "Clear",
|
||||||
|
onclick: () => this.clear(),
|
||||||
|
}),
|
||||||
|
$el("button", {
|
||||||
|
type: "button",
|
||||||
|
textContent: "Export logs...",
|
||||||
|
onclick: () => this.export(),
|
||||||
|
}),
|
||||||
|
$el("button", {
|
||||||
|
type: "button",
|
||||||
|
textContent: "View exported logs...",
|
||||||
|
onclick: () => this.import(),
|
||||||
|
}),
|
||||||
|
...super.createButtons(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getTypeColor(type) {
|
||||||
|
switch (type) {
|
||||||
|
case "error":
|
||||||
|
return "red";
|
||||||
|
case "warn":
|
||||||
|
return "orange";
|
||||||
|
case "debug":
|
||||||
|
return "dodgerblue";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show(entries) {
|
||||||
|
if (!entries) entries = this.logging.entries;
|
||||||
|
this.element.style.width = "100%";
|
||||||
|
const cols = {
|
||||||
|
source: "Source",
|
||||||
|
type: "Type",
|
||||||
|
timestamp: "Timestamp",
|
||||||
|
message: "Message",
|
||||||
|
};
|
||||||
|
const keys = Object.keys(cols);
|
||||||
|
const headers = Object.values(cols).map((title) =>
|
||||||
|
$el("div.comfy-logging-title", {
|
||||||
|
textContent: title,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const rows = entries.map((entry, i) => {
|
||||||
|
return $el(
|
||||||
|
"div.comfy-logging-log",
|
||||||
|
{
|
||||||
|
$: (el) => el.style.setProperty("--row-bg", `var(--tr-${i % 2 ? "even" : "odd"}-bg-color)`),
|
||||||
|
},
|
||||||
|
keys.map((key) => {
|
||||||
|
let v = entry[key];
|
||||||
|
let color;
|
||||||
|
if (key === "type") {
|
||||||
|
color = this.getTypeColor(v);
|
||||||
|
} else {
|
||||||
|
v = jsonReplacer(key, v, true);
|
||||||
|
|
||||||
|
if (typeof v === "object") {
|
||||||
|
v = stringify(v, 5, jsonReplacer, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $el("div", {
|
||||||
|
style: {
|
||||||
|
color,
|
||||||
|
},
|
||||||
|
textContent: v,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const grid = $el(
|
||||||
|
"div.comfy-logging-logs",
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
gridTemplateColumns: `repeat(${headers.length}, 1fr)`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[...headers, ...rows]
|
||||||
|
);
|
||||||
|
const els = [grid];
|
||||||
|
if (!this.logging.enabled) {
|
||||||
|
els.unshift(
|
||||||
|
$el("h3", {
|
||||||
|
style: { textAlign: "center" },
|
||||||
|
textContent: "Logging is disabled",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
super.show($el("div", els));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ComfyLogging {
|
||||||
|
/**
|
||||||
|
* @type Array<{ source: string, type: string, timestamp: Date, message: any }>
|
||||||
|
*/
|
||||||
|
entries = [];
|
||||||
|
|
||||||
|
#enabled;
|
||||||
|
#console = {};
|
||||||
|
|
||||||
|
get enabled() {
|
||||||
|
return this.#enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
set enabled(value) {
|
||||||
|
if (value === this.#enabled) return;
|
||||||
|
if (value) {
|
||||||
|
this.patchConsole();
|
||||||
|
} else {
|
||||||
|
this.unpatchConsole();
|
||||||
|
}
|
||||||
|
this.#enabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(app) {
|
||||||
|
this.app = app;
|
||||||
|
|
||||||
|
this.dialog = new ComfyLoggingDialog(this);
|
||||||
|
this.addSetting();
|
||||||
|
this.catchUnhandled();
|
||||||
|
this.addInitData();
|
||||||
|
}
|
||||||
|
|
||||||
|
addSetting() {
|
||||||
|
const settingId = "Comfy.Logging.Enabled";
|
||||||
|
const htmlSettingId = settingId.replaceAll(".", "-");
|
||||||
|
const setting = this.app.ui.settings.addSetting({
|
||||||
|
id: settingId,
|
||||||
|
name: settingId,
|
||||||
|
defaultValue: true,
|
||||||
|
type: (name, setter, value) => {
|
||||||
|
return $el("tr", [
|
||||||
|
$el("td", [
|
||||||
|
$el("label", {
|
||||||
|
textContent: "Logging",
|
||||||
|
for: htmlSettingId,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
$el("td", [
|
||||||
|
$el("input", {
|
||||||
|
id: htmlSettingId,
|
||||||
|
type: "checkbox",
|
||||||
|
checked: value,
|
||||||
|
onchange: (event) => {
|
||||||
|
setter((this.enabled = event.target.checked));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
$el("button", {
|
||||||
|
textContent: "View Logs",
|
||||||
|
onclick: () => {
|
||||||
|
this.app.ui.settings.element.close();
|
||||||
|
this.dialog.show();
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fontSize: "14px",
|
||||||
|
display: "block",
|
||||||
|
marginTop: "5px",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.enabled = setting.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
patchConsole() {
|
||||||
|
// Capture common console outputs
|
||||||
|
const self = this;
|
||||||
|
for (const type of ["log", "warn", "error", "debug"]) {
|
||||||
|
const orig = console[type];
|
||||||
|
this.#console[type] = orig;
|
||||||
|
console[type] = function () {
|
||||||
|
orig.apply(console, arguments);
|
||||||
|
self.addEntry("console", type, ...arguments);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unpatchConsole() {
|
||||||
|
// Restore original console functions
|
||||||
|
for (const type of Object.keys(this.#console)) {
|
||||||
|
console[type] = this.#console[type];
|
||||||
|
}
|
||||||
|
this.#console = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
catchUnhandled() {
|
||||||
|
// Capture uncaught errors
|
||||||
|
window.addEventListener("error", (e) => {
|
||||||
|
this.addEntry("window", "error", e.error ?? "Unknown error");
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("unhandledrejection", (e) => {
|
||||||
|
this.addEntry("unhandledrejection", "error", e.reason ?? "Unknown error");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.entries = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
addEntry(source, type, ...args) {
|
||||||
|
if (this.enabled) {
|
||||||
|
this.entries.push({
|
||||||
|
source,
|
||||||
|
type,
|
||||||
|
timestamp: new Date(),
|
||||||
|
message: args,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log(source, ...args) {
|
||||||
|
this.addEntry(source, "log", ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addInitData() {
|
||||||
|
if (!this.enabled) return;
|
||||||
|
const source = "ComfyUI.Logging";
|
||||||
|
this.addEntry(source, "debug", { UserAgent: navigator.userAgent });
|
||||||
|
const systemStats = await api.getSystemStats();
|
||||||
|
this.addEntry(source, "debug", systemStats);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import {api} from "./api.js";
|
import { api } from "./api.js";
|
||||||
|
|
||||||
export function $el(tag, propsOrChildren, children) {
|
export function $el(tag, propsOrChildren, children) {
|
||||||
const split = tag.split(".");
|
const split = tag.split(".");
|
||||||
@ -11,7 +11,7 @@ export function $el(tag, propsOrChildren, children) {
|
|||||||
if (Array.isArray(propsOrChildren)) {
|
if (Array.isArray(propsOrChildren)) {
|
||||||
element.append(...propsOrChildren);
|
element.append(...propsOrChildren);
|
||||||
} else {
|
} else {
|
||||||
const {parent, $: cb, dataset, style} = propsOrChildren;
|
const { parent, $: cb, dataset, style } = propsOrChildren;
|
||||||
delete propsOrChildren.parent;
|
delete propsOrChildren.parent;
|
||||||
delete propsOrChildren.$;
|
delete propsOrChildren.$;
|
||||||
delete propsOrChildren.dataset;
|
delete propsOrChildren.dataset;
|
||||||
@ -178,7 +178,7 @@ export class ComfyDialog {
|
|||||||
return [
|
return [
|
||||||
$el("button", {
|
$el("button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Close",
|
textContent: i18next.t("ui.close_btn"),
|
||||||
onclick: () => this.close(),
|
onclick: () => this.close(),
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
@ -206,11 +206,11 @@ class ComfySettingsDialog extends ComfyDialog {
|
|||||||
parent: document.body,
|
parent: document.body,
|
||||||
}, [
|
}, [
|
||||||
$el("table.comfy-modal-content.comfy-table", [
|
$el("table.comfy-modal-content.comfy-table", [
|
||||||
$el("caption", {textContent: "Settings"}),
|
$el("caption", { textContent: i18next.t("ui.settings.title") }),
|
||||||
$el("tbody", {$: (tbody) => (this.textElement = tbody)}),
|
$el("tbody", { $: (tbody) => (this.textElement = tbody) }),
|
||||||
$el("button", {
|
$el("button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Close",
|
textContent: i18next.t("ui.close_btn"),
|
||||||
style: {
|
style: {
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
},
|
},
|
||||||
@ -234,7 +234,7 @@ class ComfySettingsDialog extends ComfyDialog {
|
|||||||
localStorage[settingId] = JSON.stringify(value);
|
localStorage[settingId] = JSON.stringify(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSetting({id, name, type, defaultValue, onChange, attrs = {}, tooltip = "",}) {
|
addSetting({ id, name, type, defaultValue, onChange, attrs = {}, tooltip = "", options = undefined }) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
throw new Error("Settings must have an ID");
|
throw new Error("Settings must have an ID");
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ class ComfySettingsDialog extends ComfyDialog {
|
|||||||
$el("label", {
|
$el("label", {
|
||||||
for: htmlID,
|
for: htmlID,
|
||||||
classList: [tooltip !== "" ? "comfy-tooltip-indicator" : ""],
|
classList: [tooltip !== "" ? "comfy-tooltip-indicator" : ""],
|
||||||
textContent: name,
|
textContent: i18next.t(`settings.${id}`),
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -337,7 +337,7 @@ class ComfySettingsDialog extends ComfyDialog {
|
|||||||
value,
|
value,
|
||||||
id: htmlID,
|
id: htmlID,
|
||||||
type: "number",
|
type: "number",
|
||||||
style: {maxWidth: "4rem"},
|
style: { maxWidth: "4rem" },
|
||||||
oninput: (e) => {
|
oninput: (e) => {
|
||||||
setter(e.target.value);
|
setter(e.target.value);
|
||||||
e.target.previousElementSibling.value = e.target.value;
|
e.target.previousElementSibling.value = e.target.value;
|
||||||
@ -347,6 +347,32 @@ class ComfySettingsDialog extends ComfyDialog {
|
|||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
|
case "combo":
|
||||||
|
element = $el("tr", [
|
||||||
|
labelCell,
|
||||||
|
$el("td", [
|
||||||
|
$el(
|
||||||
|
"select",
|
||||||
|
{
|
||||||
|
oninput: (e) => {
|
||||||
|
setter(e.target.value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
(typeof options === "function" ? options(value) : options || []).map((opt) => {
|
||||||
|
if (typeof opt === "string") {
|
||||||
|
opt = { text: opt };
|
||||||
|
}
|
||||||
|
const v = opt.value ?? opt.text;
|
||||||
|
return $el("option", {
|
||||||
|
value: v,
|
||||||
|
textContent: opt.text,
|
||||||
|
selected: value + "" === v + "",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
break;
|
||||||
case "text":
|
case "text":
|
||||||
default:
|
default:
|
||||||
if (type !== "text") {
|
if (type !== "text") {
|
||||||
@ -391,10 +417,10 @@ class ComfySettingsDialog extends ComfyDialog {
|
|||||||
show() {
|
show() {
|
||||||
this.textElement.replaceChildren(
|
this.textElement.replaceChildren(
|
||||||
$el("tr", {
|
$el("tr", {
|
||||||
style: {display: "none"},
|
style: { display: "none" },
|
||||||
}, [
|
}, [
|
||||||
$el("th"),
|
$el("th"),
|
||||||
$el("th", {style: {width: "33%"}})
|
$el("th", { style: { width: "33%" } })
|
||||||
]),
|
]),
|
||||||
...this.settings.map((s) => s.render()),
|
...this.settings.map((s) => s.render()),
|
||||||
)
|
)
|
||||||
@ -431,7 +457,7 @@ class ComfyList {
|
|||||||
name: "Delete",
|
name: "Delete",
|
||||||
cb: () => api.deleteItem(this.#type, item.prompt[1]),
|
cb: () => api.deleteItem(this.#type, item.prompt[1]),
|
||||||
};
|
};
|
||||||
return $el("div", {textContent: item.prompt[0] + ": "}, [
|
return $el("div", { textContent: item.prompt[0] + ": " }, [
|
||||||
$el("button", {
|
$el("button", {
|
||||||
textContent: "Load",
|
textContent: "Load",
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
@ -460,7 +486,7 @@ class ComfyList {
|
|||||||
await this.load();
|
await this.load();
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
$el("button", {textContent: "Refresh", onclick: () => this.load()}),
|
$el("button", { textContent: "Refresh", onclick: () => this.load() }),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -480,7 +506,7 @@ class ComfyList {
|
|||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.element.style.display = "none";
|
this.element.style.display = "none";
|
||||||
this.button.textContent = "See " + this.#text;
|
this.button.textContent = "View " + this.#text;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
@ -553,14 +579,14 @@ export class ComfyUI {
|
|||||||
id: "comfy-file-input",
|
id: "comfy-file-input",
|
||||||
type: "file",
|
type: "file",
|
||||||
accept: ".json,image/png,.latent,.safetensors",
|
accept: ".json,image/png,.latent,.safetensors",
|
||||||
style: {display: "none"},
|
style: { display: "none" },
|
||||||
parent: document.body,
|
parent: document.body,
|
||||||
onchange: () => {
|
onchange: () => {
|
||||||
app.handleFile(fileInput.files[0]);
|
app.handleFile(fileInput.files[0]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.menuContainer = $el("div.comfy-menu", {parent: document.body}, [
|
this.menuContainer = $el("div.comfy-menu", { parent: document.body }, [
|
||||||
$el("div.drag-handle", {
|
$el("div.drag-handle", {
|
||||||
style: {
|
style: {
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
@ -570,16 +596,16 @@ export class ComfyUI {
|
|||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
$el("span.drag-handle"),
|
$el("span.drag-handle"),
|
||||||
$el("span", {$: (q) => (this.queueSize = q)}),
|
$el("span", { $: (q) => (this.queueSize = q) }),
|
||||||
$el("button.comfy-settings-btn", {textContent: "⚙️", onclick: () => this.settings.show()}),
|
$el("button.comfy-settings-btn", { textContent: "⚙️", onclick: () => this.settings.show() }),
|
||||||
]),
|
]),
|
||||||
$el("button.comfy-queue-btn", {
|
$el("button.comfy-queue-btn", {
|
||||||
id: "queue-button",
|
id: "queue-button",
|
||||||
textContent: "Queue Prompt",
|
textContent: i18next.t("ui.queue_btn"),
|
||||||
onclick: () => app.queuePrompt(0, this.batchCount),
|
onclick: () => app.queuePrompt(0, this.batchCount),
|
||||||
}),
|
}),
|
||||||
$el("div", {}, [
|
$el("div", {}, [
|
||||||
$el("label", {innerHTML: "Extra options"}, [
|
$el("label", { innerHTML: i18next.t("ui.extra_options") }, [
|
||||||
$el("input", {
|
$el("input", {
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
onchange: (i) => {
|
onchange: (i) => {
|
||||||
@ -590,14 +616,14 @@ export class ComfyUI {
|
|||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
$el("div", {id: "extraOptions", style: {width: "100%", display: "none"}}, [
|
$el("div", { id: "extraOptions", style: { width: "100%", display: "none" } }, [
|
||||||
$el("label", {innerHTML: "Batch count"}, [
|
$el("label", { innerHTML: "Batch count" }, [
|
||||||
$el("input", {
|
$el("input", {
|
||||||
id: "batchCountInputNumber",
|
id: "batchCountInputNumber",
|
||||||
type: "number",
|
type: "number",
|
||||||
value: this.batchCount,
|
value: this.batchCount,
|
||||||
min: "1",
|
min: "1",
|
||||||
style: {width: "35%", "margin-left": "0.4em"},
|
style: { width: "35%", "margin-left": "0.4em" },
|
||||||
oninput: (i) => {
|
oninput: (i) => {
|
||||||
this.batchCount = i.target.value;
|
this.batchCount = i.target.value;
|
||||||
document.getElementById("batchCountInputRange").value = this.batchCount;
|
document.getElementById("batchCountInputRange").value = this.batchCount;
|
||||||
@ -625,13 +651,13 @@ export class ComfyUI {
|
|||||||
$el("div.comfy-menu-btns", [
|
$el("div.comfy-menu-btns", [
|
||||||
$el("button", {
|
$el("button", {
|
||||||
id: "queue-front-button",
|
id: "queue-front-button",
|
||||||
textContent: "Queue Front",
|
textContent: i18next.t("ui.queue_front_btn"),
|
||||||
onclick: () => app.queuePrompt(-1, this.batchCount)
|
onclick: () => app.queuePrompt(-1, this.batchCount)
|
||||||
}),
|
}),
|
||||||
$el("button", {
|
$el("button", {
|
||||||
$: (b) => (this.queue.button = b),
|
$: (b) => (this.queue.button = b),
|
||||||
id: "comfy-view-queue-button",
|
id: "comfy-view-queue-button",
|
||||||
textContent: "View Queue",
|
textContent: i18next.t("ui.view_queue_btn"),
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
this.history.hide();
|
this.history.hide();
|
||||||
this.queue.toggle();
|
this.queue.toggle();
|
||||||
@ -640,7 +666,7 @@ export class ComfyUI {
|
|||||||
$el("button", {
|
$el("button", {
|
||||||
$: (b) => (this.history.button = b),
|
$: (b) => (this.history.button = b),
|
||||||
id: "comfy-view-history-button",
|
id: "comfy-view-history-button",
|
||||||
textContent: "View History",
|
textContent: i18next.t("ui.view_history_btn"),
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
this.queue.hide();
|
this.queue.hide();
|
||||||
this.history.toggle();
|
this.history.toggle();
|
||||||
@ -651,7 +677,7 @@ export class ComfyUI {
|
|||||||
this.history.element,
|
this.history.element,
|
||||||
$el("button", {
|
$el("button", {
|
||||||
id: "comfy-save-button",
|
id: "comfy-save-button",
|
||||||
textContent: "Save",
|
textContent: i18next.t("ui.save_btn"),
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
let filename = "workflow.json";
|
let filename = "workflow.json";
|
||||||
if (promptFilename.value) {
|
if (promptFilename.value) {
|
||||||
@ -662,12 +688,12 @@ export class ComfyUI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const json = JSON.stringify(app.graph.serialize(), null, 2); // convert the data to a JSON string
|
const json = JSON.stringify(app.graph.serialize(), null, 2); // convert the data to a JSON string
|
||||||
const blob = new Blob([json], {type: "application/json"});
|
const blob = new Blob([json], { type: "application/json" });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = $el("a", {
|
const a = $el("a", {
|
||||||
href: url,
|
href: url,
|
||||||
download: filename,
|
download: filename,
|
||||||
style: {display: "none"},
|
style: { display: "none" },
|
||||||
parent: document.body,
|
parent: document.body,
|
||||||
});
|
});
|
||||||
a.click();
|
a.click();
|
||||||
@ -680,7 +706,7 @@ export class ComfyUI {
|
|||||||
$el("button", {
|
$el("button", {
|
||||||
id: "comfy-dev-save-api-button",
|
id: "comfy-dev-save-api-button",
|
||||||
textContent: "Save (API Format)",
|
textContent: "Save (API Format)",
|
||||||
style: {width: "100%", display: "none"},
|
style: { width: "100%", display: "none" },
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
let filename = "workflow_api.json";
|
let filename = "workflow_api.json";
|
||||||
if (promptFilename.value) {
|
if (promptFilename.value) {
|
||||||
@ -690,14 +716,14 @@ export class ComfyUI {
|
|||||||
filename += ".json";
|
filename += ".json";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.graphToPrompt().then(p=>{
|
app.graphToPrompt().then(p => {
|
||||||
const json = JSON.stringify(p.output, null, 2); // convert the data to a JSON string
|
const json = JSON.stringify(p.output, null, 2); // convert the data to a JSON string
|
||||||
const blob = new Blob([json], {type: "application/json"});
|
const blob = new Blob([json], { type: "application/json" });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = $el("a", {
|
const a = $el("a", {
|
||||||
href: url,
|
href: url,
|
||||||
download: filename,
|
download: filename,
|
||||||
style: {display: "none"},
|
style: { display: "none" },
|
||||||
parent: document.body,
|
parent: document.body,
|
||||||
});
|
});
|
||||||
a.click();
|
a.click();
|
||||||
@ -708,15 +734,15 @@ export class ComfyUI {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
$el("button", {id: "comfy-load-button", textContent: "Load", onclick: () => fileInput.click()}),
|
$el("button", { id: "comfy-load-button", textContent: i18next.t("ui.load_btn"), onclick: () => fileInput.click() }),
|
||||||
$el("button", {
|
$el("button", {
|
||||||
id: "comfy-refresh-button",
|
id: "comfy-refresh-button",
|
||||||
textContent: "Refresh",
|
textContent: i18next.t("ui.refresh_btn"),
|
||||||
onclick: () => app.refreshComboInNodes()
|
onclick: () => app.refreshComboInNodes()
|
||||||
}),
|
}),
|
||||||
$el("button", {id: "comfy-clipspace-button", textContent: "Clipspace", onclick: () => app.openClipspace()}),
|
$el("button", { id: "comfy-clipspace-button", textContent: i18next.t("ui.clipspace_btn"), onclick: () => app.openClipspace() }),
|
||||||
$el("button", {
|
$el("button", {
|
||||||
id: "comfy-clear-button", textContent: "Clear", onclick: () => {
|
id: "comfy-clear-button", textContent: i18next.t("ui.clear_btn"), onclick: () => {
|
||||||
if (!confirmClear.value || confirm("Clear workflow?")) {
|
if (!confirmClear.value || confirm("Clear workflow?")) {
|
||||||
app.clean();
|
app.clean();
|
||||||
app.graph.clear();
|
app.graph.clear();
|
||||||
@ -724,7 +750,7 @@ export class ComfyUI {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$el("button", {
|
$el("button", {
|
||||||
id: "comfy-load-default-button", textContent: "Load Default", onclick: () => {
|
id: "comfy-load-default-button", textContent: i18next.t("ui.load_default_btn"), onclick: () => {
|
||||||
if (!confirmClear.value || confirm("Load default workflow?")) {
|
if (!confirmClear.value || confirm("Load default workflow?")) {
|
||||||
app.loadGraphData()
|
app.loadGraphData()
|
||||||
}
|
}
|
||||||
@ -737,16 +763,16 @@ export class ComfyUI {
|
|||||||
name: "Enable Dev mode Options",
|
name: "Enable Dev mode Options",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
onChange: function(value) { document.getElementById("comfy-dev-save-api-button").style.display = value ? "block" : "none"},
|
onChange: function (value) { document.getElementById("comfy-dev-save-api-button").style.display = value ? "block" : "none" },
|
||||||
});
|
});
|
||||||
|
|
||||||
dragElement(this.menuContainer, this.settings);
|
dragElement(this.menuContainer, this.settings);
|
||||||
|
|
||||||
this.setStatus({exec_info: {queue_remaining: "X"}});
|
this.setStatus({ exec_info: { queue_remaining: "X" } });
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(status) {
|
setStatus(status) {
|
||||||
this.queueSize.textContent = "Queue size: " + (status ? status.exec_info.queue_remaining : "ERR");
|
this.queueSize.textContent = i18next.t("ui.queue_size") + (status ? status.exec_info.queue_remaining : "ERR");
|
||||||
if (status) {
|
if (status) {
|
||||||
if (
|
if (
|
||||||
this.lastQueueSize != 0 &&
|
this.lastQueueSize != 0 &&
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user