diff --git a/movie_editor/backend/nodes.py b/movie_editor/backend/nodes.py index 7c68cf0c..4b66ebc0 100644 --- a/movie_editor/backend/nodes.py +++ b/movie_editor/backend/nodes.py @@ -116,10 +116,14 @@ def connection_inputs(node_def: dict) -> list[dict]: if not isinstance(t, str): continue opts = spec[1] if len(spec) > 1 and isinstance(spec[1], dict) else {} - # A widget-typed input is a SOCKET (wireable) only when forceInput is set — - # e.g. refinement_key_input is ("STRING", {"forceInput": True}). Plain widgets - # are handled by widget_inputs, not here. - if t in _WIDGET_TYPES and not opts.get("forceInput"): + if t in _WIDGET_TYPES: + # A widget-typed input is a SOCKET (wireable) only when forceInput is set — + # e.g. refinement_key_input is ("STRING", {"forceInput": True}). It always + # has a widget fallback, so it's NEVER auto-required (it must not block + # generation when left unwired). Plain widgets are handled by widget_inputs. + if not opts.get("forceInput"): + continue + out.append({"name": name, "type": _normalize_type(t), "required": False}) continue # V3 nodes can mark a required-group input as optional via the flag. is_required = group == "required" and not opts.get("optional", False) diff --git a/movie_editor/frontend/models.js b/movie_editor/frontend/models.js index 825293d0..95082092 100644 --- a/movie_editor/frontend/models.js +++ b/movie_editor/frontend/models.js @@ -735,9 +735,9 @@ function body() { const b = el("div", "models-body"); b.append(requirementsPanel()); + b.append(coreSection()); b.append(composer()); b.append(linksSection()); - b.append(coreSection()); const v = validation(); const list = el("div", "slot-list"); if (!config.slots.length) list.append(el("div", "empty-stage", "No models configured yet — use the form above to add loaders.")); diff --git a/movie_editor/frontend/store.js b/movie_editor/frontend/store.js index 7ee52d16..e0f0710c 100644 --- a/movie_editor/frontend/store.js +++ b/movie_editor/frontend/store.js @@ -294,12 +294,14 @@ [/\.vae.*no source/i, "Missing Video VAE — add a Video VAE loader in Models."], [/\.model.*no source/i, "Missing diffusion model — add a Unet loader in Models."], [/\.clip.*no source/i, "Missing text encoder — add a CLIP loader in Models."], - [/no source available/i, "A required pipeline input has no source — open Models and check the Pipeline requirements checklist."], [/not installed/i, "A configured node class is not installed in ComfyUI — check Models for details."], [/Node registry unavailable/i, "ComfyUI is offline or still starting up — wait a moment and try again."], ]; function _friendlyGenError(raw) { + // "Generation blocked — …" already names the exact input(s); pass it through so the + // message is actionable instead of a vague "a required input has no source". + if (/Generation blocked/i.test(raw)) return raw; for (const [pat, msg] of GEN_ERROR_MAP) if (pat.test(raw)) return msg; return raw; }