core+shell: the bridge, and the first module through it

One declaration does three jobs: renders the panel, carries the defaults, names
the values that come back. A module writes no DOM and reads no DOM.

Validation refuses rather than repairs -- unknown type, a renderer its type does
not have, a label that is not text, a default outside its own bounds, a when
naming a key the module never declared. A module that is wrong does not get a
best-effort panel; it does not load, and says why once.

Ordering is by declared relation and stage, never priority numbers, and a cycle
drops only the modules in it. Compatibility is by trait, never model name.

The shell hands a module { composer, values, on, shell } and nothing else: no
node, no document, no kit internals. It cannot style anything because it is
never given anything that could.

catalogue/hide.html puts one good module and five broken ones through the real
shell and shows the result: one panel, five absent, no placeholders. The lint
fixtures make the scanner prove it catches each rule, so a clean scan means
something.

First slice: modules/system/appearance. Two settings, an optional ui.js, and
clicking Light actually turns the app light.
This commit is contained in:
digital-garbage
2026-08-28 19:58:06 +03:00
parent 73e81b0263
commit 835fab7864
54 changed files with 2219 additions and 45 deletions
+13 -4
View File
@@ -6,23 +6,32 @@ exercised every time you look at the catalogue.
python tools/devserver.py [port]
"""
import json
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
from urllib.parse import unquote, urlparse
from urllib.parse import parse_qs, unquote, urlparse
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from core import config, serve as static # noqa: E402
from core import config, routes, serve as static # noqa: E402
P = config.UI_PREFIX
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
path = unquote(urlparse(self.path).path)
parsed = urlparse(self.path)
path = unquote(parsed.path)
if path in (P, P + "/"):
# The same manifest the aiohttp route serves, so what you see in the
# browser here is what ComfyUI would send.
if path == P + "/api/modules":
raw = parse_qs(parsed.query).get("traits")
traits = [t for t in raw[0].split(",") if t] if raw else None
body = json.dumps(routes.manifest(traits)).encode()
served = static.Served(200, body, "application/json")
elif path in (P, P + "/"):
served = static.serve(config.APP_DIR, "index.html", config.APP_EXTS)
elif path.startswith(P + "/app/"):
served = static.serve(config.APP_DIR, path[len(P) + 5:], config.APP_EXTS)