diff --git a/py/config.py b/py/config.py index 0863a6d..a4af8a6 100644 --- a/py/config.py +++ b/py/config.py @@ -2,7 +2,7 @@ import os import json import re -from .utils import get_dict_value, set_dict_value, dict_has_key +from .utils import get_dict_value, set_dict_value, dict_has_key, load_json_file def get_config_value(key): return get_dict_value(RGTHREE_CONFIG, key) @@ -32,18 +32,11 @@ def set_user_config(data: dict): def get_rgthree_default_config(): """ Gets the default configuration.""" - with open(DEFAULT_CONFIG_FILE, 'r', encoding = 'UTF-8') as file: - config = re.sub(r"(?:^|\s)//.*", "", file.read(), flags=re.MULTILINE) - return json.loads(config) + return load_json_file(DEFAULT_CONFIG_FILE, default={}) def get_rgthree_user_config(): """ Gets the user configuration.""" - if os.path.exists(USER_CONFIG_FILE): - with open(USER_CONFIG_FILE, 'r', encoding = 'UTF-8') as file: - config = re.sub(r"(?:^|\s)//.*", "", file.read(), flags=re.MULTILINE) - return json.loads(config) - else: - return {} + return load_json_file(USER_CONFIG_FILE, default={}) def write_user_config(): """ Writes the user configuration.""" diff --git a/py/power_prompt_utils.py b/py/power_prompt_utils.py index 8f21533..4f1fe9e 100644 --- a/py/power_prompt_utils.py +++ b/py/power_prompt_utils.py @@ -47,7 +47,7 @@ def get_lora_by_filename(file_path, lora_paths=None, log_node=None): return found # Same check, but ensure file_path is without extension. - file_path_force_no_ext = os.path.splitext(file_path) + file_path_force_no_ext = os.path.splitext(file_path)[0] if file_path_force_no_ext in lora_paths_no_ext: found = lora_paths[lora_paths_no_ext.index(file_path_force_no_ext)] return found diff --git a/py/utils.py b/py/utils.py index e8b7603..fe4a87c 100644 --- a/py/utils.py +++ b/py/utils.py @@ -1,3 +1,7 @@ +import json +import os +import re + class AnyType(str): """A special class that is always equal in not equal comparisons. Credit to pythongosssss""" @@ -5,16 +9,24 @@ class AnyType(str): def __ne__(self, __value: object) -> bool: return False + class ContainsAnyDict(dict): """A special class that always returns true for contains check ('prop' in my_dict).""" def __contains__(self, key): return True + any_type = AnyType("*") -def get_dict_value(data: dict, dict_key: str, default = None): +def is_dict_value_falsy(data: dict, dict_key: str): + """ Checks if a dict value is falsy.""" + val = get_dict_value(data, dict_key) + return not val + + +def get_dict_value(data: dict, dict_key: str, default=None): """ Gets a deeply nested value given a dot-delimited key.""" keys = dict_key.split('.') key = keys.pop(0) if len(keys) > 0 else None @@ -24,7 +36,7 @@ def get_dict_value(data: dict, dict_key: str, default = None): return found if found is not None else default -def set_dict_value(data: dict, dict_key: str, value, create_missing_objects = True): +def set_dict_value(data: dict, dict_key: str, value, create_missing_objects=True): """ Sets a deeply nested value given a dot-delimited key.""" keys = dict_key.split('.') key = keys.pop(0) if len(keys) > 0 else None @@ -39,6 +51,7 @@ def set_dict_value(data: dict, dict_key: str, value, create_missing_objects = Tr return data + def dict_has_key(data: dict, dict_key): """ Checks if a dict has a deeply nested dot-delimited key.""" keys = dict_key.split('.') @@ -48,3 +61,12 @@ def dict_has_key(data: dict, dict_key): if len(keys) == 0: return True return dict_has_key(data[key], '.'.join(keys)) + + +def load_json_file(file: str, default=None): + """Reads a json file and returns the json dict, stripping out "//" comments first.""" + if os.path.exists(file): + with open(file, 'r', encoding='UTF-8') as file: + config = re.sub(r"(?:^|\s)//.*", "", file.read(), flags=re.MULTILINE) + return json.loads(config) + return default diff --git a/src_web/comfyui/rgthree.scss b/src_web/comfyui/rgthree.scss index 13b88f2..fc665a9 100644 --- a/src_web/comfyui/rgthree.scss +++ b/src_web/comfyui/rgthree.scss @@ -112,115 +112,6 @@ z-index: 3 !important; /* This is set to 1, but goes under the multi-line inputs, so bump it. */ } -.rgthree-dialog { - outline: 0; - border: 0; - border-radius: 6px; - background: #414141; - color: #fff; - box-shadow: - inset 1px 1px 0px rgba(255, 255, 255, 0.05), - inset -1px -1px 0px rgba(0, 0, 0, 0.5), - 2px 2px 20px rgb(0, 0, 0); - max-width: 800px; - box-sizing: border-box; - font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; - font-size: 1rem; - padding: 0; - max-height: calc(100% - 32px); -} -.rgthree-dialog-container { - padding: 16px; -} - -.rgthree-dialog.-iconed::after { - content: ""; - font-size: 276px; - position: absolute; - right: 0px; - bottom: 0px; - opacity: 0.15; - display: block; - width: 237px; - overflow: hidden; - height: 186px; - line-height: 1; - pointer-events: none; - z-index: -1; -} -.rgthree-dialog.-iconed.-help::after { - content: "🛟"; -} -.rgthree-dialog.-iconed.-settings::after { - content: "⚙️"; -} - -@media (max-width: 832px) { - .rgthree-dialog { - max-width: calc(100% - 32px); - } -} - -.rgthree-dialog-container-title { - margin-bottom: 16px; - display: flex; - flex-direction: row; - align-items: center; - justify-content: start; -} -.rgthree-dialog-container-title > svg:first-child { - width: 36px; - height: 36px; - margin-right: 16px; -} -.rgthree-dialog-container-title h2 { - font-size: calc(22rem / 16); - margin: 0; - font-weight: bold; -} - -.rgthree-dialog-container-title h2 small { - font-size: calc(13rem / 16); - font-weight: normal; - opacity: 0.75; -} - -.rgthree-dialog-container-content { - overflow: auto; - max-height: calc(100vh - 200px); /* Arbitrary height to copensate for margin, title, and footer.*/ -} -.rgthree-dialog-container-content p { - font-size: calc(13rem / 16); - margin-top: 0; -} - -.rgthree-dialog-container-content ul li p { - margin-bottom: 4px; -} - -.rgthree-dialog-container-content ul li p + p { - margin-top: 0.5em; -} - -.rgthree-dialog-container-content ul li ul { - margin-top: 0.5em; - margin-bottom: 1em; -} - -.rgthree-dialog-container-content p code { - display: inline-block; - padding: 2px 4px; - margin: 0px 2px; - border: 1px solid rgba(255, 255, 255, 0.25); - border-radius: 3px; - background: rgba(255, 255, 255, 0.1); -} - -.rgthree-dialog-container-footer { - display: flex; - align-items: center; - justify-content: center; -} @import '../common/css/buttons.scss'; @import '../common/css/dialog.scss'; diff --git a/src_web/comfyui/utils_canvas.ts b/src_web/comfyui/utils_canvas.ts index b85339c..5bf4c74 100644 --- a/src_web/comfyui/utils_canvas.ts +++ b/src_web/comfyui/utils_canvas.ts @@ -141,6 +141,7 @@ type DrawNumberWidgetPartOptions = { height: number; value: number; direction?: 1 | -1; + textColor?: string; }; /** @@ -165,7 +166,7 @@ export function drawNumberWidgetPart( ctx.save(); let posX = options.posX; - const { posY, height, value } = options; + const { posY, height, value, textColor } = options; const midY = posY + height / 2; // If we're drawing parts from right to left (usually when something in the middle will be @@ -190,7 +191,12 @@ export function drawNumberWidgetPart( // Draw the strength text. ctx.textAlign = "center"; ctx.textBaseline = "middle"; + const oldTextcolor = ctx.fillStyle; + if (textColor) { + ctx.fillStyle = textColor; + } ctx.fillText(fitString(ctx, value.toFixed(2), numberWidth), posX + numberWidth / 2, midY); + ctx.fillStyle = oldTextcolor; xBoundsNumber[0] = posX; xBoundsNumber[1] = numberWidth; diff --git a/src_web/common/css/dialog.scss b/src_web/common/css/dialog.scss index 7b0189d..a8766f1 100644 --- a/src_web/common/css/dialog.scss +++ b/src_web/common/css/dialog.scss @@ -16,8 +16,23 @@ padding: 0; max-height: calc(100% - 32px); } + +*, *::before, *::after { + box-sizing: inherit; +} + .rgthree-dialog-container { - padding: 16px; + // padding: 16px; + > * { + padding: 8px 16px; + + &:first-child { + padding-top: 16px; + } + &:last-child { + padding-bottom: 16px; + } + } } .rgthree-dialog.-iconed::after { @@ -49,7 +64,6 @@ } .rgthree-dialog-container-title { - margin-bottom: 16px; display: flex; flex-direction: row; align-items: center; @@ -112,4 +126,3 @@ body.rgthree-dialog-open > *:not(.rgthree-dialog):not(.rgthree-top-messages-container) { filter: blur(5px); } - diff --git a/src_web/common/dialog.ts b/src_web/common/dialog.ts index 3ad98f2..96e7de9 100644 --- a/src_web/common/dialog.ts +++ b/src_web/common/dialog.ts @@ -1,5 +1,5 @@ import type { LGraphNode, LGraphNodeConstructor } from "typings/litegraph.js"; -import { createElement as $el, getClosestOrSelf } from "./utils_dom.js"; +import { createElement as $el, getClosestOrSelf, setAttributes } from "./utils_dom.js"; type RgthreeDialogButton = { label: string; @@ -27,6 +27,7 @@ export type RgthreeDialogOptions = { export class RgthreeDialog extends EventTarget { element: HTMLDialogElement; contentElement: HTMLDivElement; + titleElement: HTMLDivElement; options: RgthreeDialogOptions; constructor(options: RgthreeDialogOptions) { @@ -53,13 +54,18 @@ export class RgthreeDialog extends EventTarget { }, }, }); + this.element.addEventListener("close", (event) => { + this.onDialogElementClose(); + }); - if (options.title) { - $el("div.rgthree-dialog-container-title", { - parent: container, - child: options.title.includes("