Compare commits

...

6 Commits
3.3.0 ... main

Author SHA1 Message Date
Yasutaka ATARASHI 8c441f9afc
Improve compatibility of dynamicprompts YAML wildcards (#339)
- Folder paths are prepended to tree structures in YAML
- Single strings are parsed as collections containing a single item
2026-03-21 11:51:51 +01:00
Redacted 7deb665416
Fix unhandled TypeErrors for SDNext (#338) 2026-02-27 10:31:12 +01:00
DominikDoom a46c5ce044 Fix import failure on some webui forks
In this case, caused by forge neo removing sd_hijack
Fixes #337
2026-02-05 21:10:19 +01:00
Serick 19a30beed4
Fix glob pattern matching for filenames with special characters (#335) 2025-11-11 11:20:18 +01:00
DominikDoom 89fee277e3 Fix model path breaking with commas in filenames
Fixes #332
2025-09-09 10:04:00 +02:00
DominikDoom c4510663ca Fix lora / embed preview being below the textbox
This was a small visual regression for the normal webui introduced by #327
Should keep the fix working for forge
2025-09-09 09:57:50 +02:00
3 changed files with 26 additions and 17 deletions

View File

@ -31,7 +31,8 @@ const autocompleteCSS = `
position: absolute;
z-index: 999;
max-width: calc(100% - 1.5rem);
flex-direction: column; /* Ensure children stack vertically */
flex-wrap: wrap;
gap: 10px;
}
.autocompleteResults {
background-color: var(--results-bg) !important;
@ -49,7 +50,6 @@ const autocompleteCSS = `
.sideInfo {
display: none;
position: relative;
margin-left: 10px;
height: 18rem;
max-width: 16rem;
}

View File

@ -37,14 +37,14 @@ except ImportError:
if not IS_FORGE_CLASSIC:
try:
HYP_PATH = Path(shared.cmd_opts.hypernetwork_dir).absolute()
except AttributeError:
except (AttributeError, TypeError):
HYP_PATH = None
else:
HYP_PATH = None
try:
LORA_PATH = Path(shared.cmd_opts.lora_dir).absolute()
except AttributeError:
except (AttributeError, TypeError):
LORA_PATH = None
try:
@ -52,7 +52,7 @@ try:
LYCO_PATH = Path(shared.cmd_opts.lyco_dir_backcompat).absolute()
except:
LYCO_PATH = Path(shared.cmd_opts.lyco_dir).absolute() # attempt original non-backcompat path
except AttributeError:
except (AttributeError, TypeError):
LYCO_PATH = None

View File

@ -14,7 +14,7 @@ import gradio as gr
import yaml
from fastapi import FastAPI
from fastapi.responses import FileResponse, JSONResponse, Response
from modules import hashes, script_callbacks, sd_hijack, sd_models, shared
from modules import hashes, script_callbacks, sd_models, shared
from pydantic import BaseModel
from scripts.model_keyword_support import (get_lora_simple_hash,
@ -22,6 +22,11 @@ from scripts.model_keyword_support import (get_lora_simple_hash,
write_model_keyword_path)
from scripts.shared_paths import *
try:
from modules import sd_hijack
except (ImportError, ModuleNotFoundError):
sd_hijack = None
try:
try:
from scripts import tag_frequency_db as tdb
@ -98,9 +103,9 @@ def sort_models(model_list, sort_method = None, name_has_subpath = False):
# During merging on the JS side we need to re-sort anyway, so here only the sort criteria are calculated.
# The list itself doesn't need to get sorted at this point.
if len(model_list[0]) > 2:
results = [f'{name},"{sorter(path, name, name_has_subpath)}",{meta}' for path, name, meta in model_list]
results = [f'"{name}","{sorter(path, name, name_has_subpath)}",{meta}' for path, name, meta in model_list]
else:
results = [f'{name},"{sorter(path, name, name_has_subpath)}"' for path, name in model_list]
results = [f'"{name}","{sorter(path, name, name_has_subpath)}"' for path, name in model_list]
return results
@ -151,17 +156,21 @@ def parse_umi_format(umi_tags, data):
count += 1
def parse_dynamic_prompt_format(yaml_wildcards, data, path):
def parse_dynamic_prompt_format(yaml_wildcards, data, path, root):
# Recurse subkeys, delete those without string lists as values
def recurse_dict(d: dict):
for key, value in d.copy().items():
if isinstance(value, dict):
recurse_dict(value)
elif not (isinstance(value, list) and all(isinstance(v, str) for v in value)):
elif (not isinstance(value, str) and # a string value is treated as a list of one item
not (isinstance(value, list) and all(isinstance(v, str) for v in value))):
del d[key]
try:
recurse_dict(data)
# prepend folder parts
for part in reversed(path.relative_to(root).parent.parts):
data = {part: data}
# Add to yaml_wildcards
yaml_wildcards[path.name] = data
except:
@ -172,14 +181,14 @@ def get_yaml_wildcards():
"""Returns a list of all tags found in extension YAML files found under a Tags: key."""
yaml_files = []
for path in WILDCARD_EXT_PATHS:
yaml_files.extend(p for p in path.rglob("*.yml") if p.is_file())
yaml_files.extend(p for p in path.rglob("*.yaml") if p.is_file())
yaml_files.extend((p, path) for p in path.rglob("*.yml") if p.is_file())
yaml_files.extend((p, path) for p in path.rglob("*.yaml") if p.is_file())
yaml_wildcards = {}
umi_tags = {} # { tag: count }
for path in yaml_files:
for path, root in yaml_files:
try:
with open(path, encoding="utf8") as file:
data = yaml.safe_load(file)
@ -187,7 +196,7 @@ def get_yaml_wildcards():
if (is_umi_format(data)):
parse_umi_format(umi_tags, data)
else:
parse_dynamic_prompt_format(yaml_wildcards, data, path)
parse_dynamic_prompt_format(yaml_wildcards, data, path, root)
else:
print('No data found in ' + path.name)
except (yaml.YAMLError, UnicodeDecodeError, AttributeError, TypeError) as e:
@ -748,7 +757,7 @@ def api_tac(_: gr.Blocks, app: FastAPI):
return Response(status_code=404)
try:
json_candidates = glob.glob(base_path.as_posix() + f"/**/{filename}.json", recursive=True)
json_candidates = glob.glob(base_path.as_posix() + f"/**/{glob.escape(filename)}.json", recursive=True)
if json_candidates is not None and len(json_candidates) > 0 and Path(json_candidates[0]).is_file():
return FileResponse(json_candidates[0])
except Exception as e:
@ -759,7 +768,7 @@ def api_tac(_: gr.Blocks, app: FastAPI):
return Response(status_code=404)
try:
img_glob = glob.glob(base_path.as_posix() + f"/**/{filename}.*", recursive=True)
img_glob = glob.glob(base_path.as_posix() + f"/**/{glob.escape(filename)}.*", recursive=True)
img_candidates = [img for img in img_glob if Path(img).suffix in [".png", ".jpg", ".jpeg", ".webp", ".gif"] and Path(img).is_file()]
if img_candidates is not None and len(img_candidates) > 0:
if blob:
@ -788,7 +797,7 @@ def api_tac(_: gr.Blocks, app: FastAPI):
@app.get("/tacapi/v1/lora-cached-hash/{lora_name}")
async def get_lora_cached_hash(lora_name: str):
path_glob = glob.glob(LORA_PATH.as_posix() + f"/**/{lora_name}.*", recursive=True)
path_glob = glob.glob(LORA_PATH.as_posix() + f"/**/{glob.escape(lora_name)}.*", recursive=True)
paths = [lora for lora in path_glob if Path(lora).suffix in [".safetensors", ".ckpt", ".pt"] and Path(lora).is_file()]
if paths is not None and len(paths) > 0:
path = paths[0]