diff --git a/.gitignore b/.gitignore index 0177e1d7d..6b28b976f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ venv/ web/extensions/* !web/extensions/logging.js.example !web/extensions/core/ +!web/extensions/i18n/ diff --git a/comfy/model_management.py b/comfy/model_management.py index 0ffca06da..4dd15b41c 100644 --- a/comfy/model_management.py +++ b/comfy/model_management.py @@ -535,7 +535,7 @@ def should_use_fp16(device=None, model_params=0): return False #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: if x in props.name: return False diff --git a/comfy/sd.py b/comfy/sd.py index 922cbf21e..2996a938b 100644 --- a/comfy/sd.py +++ b/comfy/sd.py @@ -70,13 +70,22 @@ def load_lora(lora, to_load): alpha = lora[alpha_name].item() loaded_keys.add(alpha_name) - A_name = "{}.lora_up.weight".format(x) - B_name = "{}.lora_down.weight".format(x) - mid_name = "{}.lora_mid.weight".format(x) + regular_lora = "{}.lora_up.weight".format(x) + diffusers_lora = "{}_lora.up.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 - if mid_name in lora.keys(): + if mid_name is not None and mid_name in lora.keys(): mid = lora[mid_name] loaded_keys.add(mid_name) 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"): key_lora = k[:-len(".weight")].replace(".", "_") 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 def set_attr(obj, attr, value): @@ -864,7 +878,7 @@ def load_controlnet(ckpt_path, model=None): use_fp16 = model_management.should_use_fp16() controlnet_config = model_detection.model_config_from_unet(controlnet_data, prefix, use_fp16).unet_config 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) if pth: diff --git a/cuda_malloc.py b/cuda_malloc.py index a808b2071..d033529cc 100644 --- a/cuda_malloc.py +++ b/cuda_malloc.py @@ -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", "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 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: names = get_gpu_names() diff --git a/notebooks/comfyui_colab.ipynb b/notebooks/comfyui_colab.ipynb index 1bb90f7d0..b1c487101 100644 --- a/notebooks/comfyui_colab.ipynb +++ b/notebooks/comfyui_colab.ipynb @@ -159,13 +159,64 @@ "\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", "metadata": { "id": "kkkkkkkkkkkkkk" }, "source": [ - "### Run ComfyUI with localtunnel (Recommended Way)\n", + "### Run ComfyUI with localtunnel\n", "\n", "\n" ] diff --git a/server.py b/server.py index f61b11a97..fab33be3e 100644 --- a/server.py +++ b/server.py @@ -345,6 +345,11 @@ class PromptServer(): 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) 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": [ { "name": device_name, diff --git a/web/extensions/core/colorPalette.js b/web/extensions/core/colorPalette.js index 3695b08e2..d414d4298 100644 --- a/web/extensions/core/colorPalette.js +++ b/web/extensions/core/colorPalette.js @@ -435,7 +435,7 @@ app.registerExtension({ $el("td", [ $el("label", { for: id.replaceAll(".", "-"), - textContent: "Color palette", + textContent: i18next.t("settings.Comfy.ColorPalette"), }), ]), $el("td", [ @@ -449,7 +449,7 @@ app.registerExtension({ }, [ $el("input", { type: "button", - value: "Export", + value: i18next.t("settings.Comfy.ColorPalette.export"), onclick: async () => { const colorPaletteId = app.ui.settings.getSettingValue(id, defaultColorPaletteId); const colorPalette = await completeColorPalette(getColorPalette(colorPaletteId)); @@ -471,14 +471,14 @@ app.registerExtension({ }), $el("input", { type: "button", - value: "Import", + value: i18next.t("settings.Comfy.ColorPalette.import"), onclick: () => { fileInput.click(); } }), $el("input", { type: "button", - value: "Template", + value: i18next.t("settings.Comfy.ColorPalette.template"), onclick: async () => { const colorPalette = await getColorPaletteTemplate(); const json = JSON.stringify(colorPalette, null, 2); // convert the data to a JSON string @@ -499,7 +499,7 @@ app.registerExtension({ }), $el("input", { type: "button", - value: "Delete", + value: i18next.t("settings.Comfy.ColorPalette.delete"), onclick: async () => { let colorPaletteId = app.ui.settings.getSettingValue(id, defaultColorPaletteId); diff --git a/web/extensions/core/contextMenuFilter.js b/web/extensions/core/contextMenuFilter.js index e0e8854b3..152cd7043 100644 --- a/web/extensions/core/contextMenuFilter.js +++ b/web/extensions/core/contextMenuFilter.js @@ -27,10 +27,13 @@ const ext = { const clickedComboValue = currentNode.widgets .filter(w => w.type === "combo" && w.options.values.length === values.length) .find(w => w.options.values.every((v, i) => v === values[i])) - .value; + ?.value; - let selectedIndex = values.findIndex(v => v === clickedComboValue); - let selectedItem = displayedItems?.[selectedIndex]; + let selectedIndex = clickedComboValue ? values.findIndex(v => v === clickedComboValue) : 0; + if (selectedIndex < 0) { + selectedIndex = 0; + } + let selectedItem = displayedItems[selectedIndex]; updateSelected(); // Apply highlighting to the selected item diff --git a/web/extensions/core/i18n.js b/web/extensions/core/i18n.js new file mode 100644 index 000000000..883fed988 --- /dev/null +++ b/web/extensions/core/i18n.js @@ -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}`) + } + } +}) \ No newline at end of file diff --git a/web/extensions/core/linkRenderMode.js b/web/extensions/core/linkRenderMode.js new file mode 100644 index 000000000..8b8d4e01f --- /dev/null +++ b/web/extensions/core/linkRenderMode.js @@ -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); diff --git a/web/extensions/core/widgetInputs.js b/web/extensions/core/widgetInputs.js index d9eaf8a0c..e4ab088dd 100644 --- a/web/extensions/core/widgetInputs.js +++ b/web/extensions/core/widgetInputs.js @@ -57,6 +57,7 @@ function convertToInput(node, widget, config) { const sz = node.size; node.addInput(widget.name, linkType, { widget: { name: widget.name, config }, + label: config?.[1] ? i18next.t(config?.[1].label) : void 0, }); for (const widget of node.widgets) { diff --git a/web/i18n/en_US.js b/web/i18n/en_US.js new file mode 100644 index 000000000..4012f47a6 --- /dev/null +++ b/web/i18n/en_US.js @@ -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", + } +} \ No newline at end of file diff --git a/web/i18n/zh_CN.js b/web/i18n/zh_CN.js new file mode 100644 index 000000000..b018a668e --- /dev/null +++ b/web/i18n/zh_CN.js @@ -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": "删除", + } +} \ No newline at end of file diff --git a/web/index.html b/web/index.html index 71067d993..c3620a134 100644 --- a/web/index.html +++ b/web/index.html @@ -6,6 +6,8 @@ + +