From 8eca2194204f5d7607ef69e8bdbf4c3e46fd9f85 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Sun, 2 Aug 2026 03:31:01 +0200 Subject: [PATCH] docs: document backend architecture and checks --- README.md | 9 ++++ docs/architecture.md | 47 +++++++++++++++++++++ tests/test_downloads_tab_workflow_route.mjs | 13 +++--- 3 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 docs/architecture.md diff --git a/README.md b/README.md index 89e9055..e8f6870 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,15 @@ Configure credentials and API keys in the Settings panel to authenticate gated d * Libraries: `requests`, `aiohttp`, `rapidfuzz`, `huggingface-hub` * Modern web browser with JS support (Chrome, Edge, Firefox, Brave) +## 🧱 Backend Architecture + +The ComfyUI entry point is intentionally thin. Runtime state lives in +`core/extension.py`, route registration is centralized in +`core/routes/registry.py`, HTTP adapters live in `core/routes/`, and reusable +feature behavior lives in `core/services/`. See +[`docs/architecture.md`](docs/architecture.md) for the dependency flow and +local verification commands. + --- ## 📜 License diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..a96b29a --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,47 @@ +# Backend architecture + +Model Resolver keeps the ComfyUI entry point small and separates HTTP wiring from +the feature logic that powers each route. + +## Runtime flow + +1. `__init__.py` exposes the ComfyUI compatibility exports and initializes the + extension. +2. `core/extension.py` owns runtime state such as progress trackers and starts + route registration. +3. `core/routes/registry.py` loads the available integrations and builds a + `RouteContext` with explicit dependencies. +4. Modules in `core/routes/` adapt HTTP requests and responses to service calls. +5. Services in `core/services/` contain feature behavior and receive their + dependencies through `RouteContext`. + +The grouping modules `core/routes/model_info.py`, `core/routes/search.py`, and +`core/routes/workflow.py` remain compatibility entry points for related route +families. The actual handlers live in focused route adapters and services. + +## Adding a route + +When adding a feature, keep the request parsing, status codes, and JSON response +in a route adapter. Put reusable behavior in a service and add its dependency to +`RouteContext` explicitly. Use `json_api_endpoint` for the common error boundary, +and preserve deliberate `aiohttp.web.HTTPException` statuses when returning +client errors. + +Add a focused route/service test before changing behavior. For changes that touch +route registration or shared backend code, run the complete Python suite as well. + +## Local verification + +Run these commands with the same Python interpreter used by ComfyUI: + +```powershell +python -m pytest +python -m ruff check __init__.py core +python -m compileall -q __init__.py core +python -m coverage run -m pytest +python -m coverage report -m +``` + +The development tools are declared in the `dev` optional dependency group in +`pyproject.toml`. Coverage configuration is kept there as well, with branch +coverage enabled and `core/` as the measured source. diff --git a/tests/test_downloads_tab_workflow_route.mjs b/tests/test_downloads_tab_workflow_route.mjs index ade0e57..85db2fd 100644 --- a/tests/test_downloads_tab_workflow_route.mjs +++ b/tests/test_downloads_tab_workflow_route.mjs @@ -106,9 +106,9 @@ test('Missing Models rows use fixed-size virtualization for smooth stable scroll assert.ok(rowRule, 'Expected the Missing Models row style'); assert.match(rowRule[1], /content-visibility\s*:\s*auto/); - assert.match(rowRule[1], /contain-intrinsic-size\s*:\s*70px/); - assert.match(rowRule[1], /height\s*:\s*70px/); - assert.match(rowRule[1], /min-height\s*:\s*70px/); + assert.match(rowRule[1], /contain-intrinsic-size\s*:\s*var\(--mr-missing-row-height\)/); + assert.match(rowRule[1], /height\s*:\s*var\(--mr-missing-row-height\)/); + assert.match(rowRule[1], /min-height\s*:\s*var\(--mr-missing-row-height\)/); assert.match(rowRule[1], /box-sizing\s*:\s*border-box/); assert.match(rowRule[1], /contain\s*:\s*layout paint style/); }); @@ -138,7 +138,7 @@ test('manual model selection uses the two-line status card layout', () => { ); assert.match( resolverMainCssSource, - /\.mr-missing-detail-pane \.model-resolver-selected\s*\{[^}]*height:\s*70px[^}]*border-radius:\s*0/s + /\.mr-missing-detail-pane \.model-resolver-selected\s*\{[^}]*height:\s*var\(--mr-missing-row-height\)[^}]*border-radius:\s*0/s ); assert.match( resolverMainCssSource, @@ -148,7 +148,10 @@ test('manual model selection uses the two-line status card layout', () => { resolverMainCssSource, /\.mr-missing-detail-pane \.mr-selected-bar-inner\s*\{[^}]*flex-direction:\s*column/s ); - assert.match(resolverMainCssSource, /\.mr-btn-danger\s*\{[^}]*rgba\(239,\s*68,\s*68/s); + assert.match( + resolverMainCssSource, + /\.mr-missing-detail-pane \.model-resolver-selected \.mr-btn-danger:hover[^}]*background:\s*rgba\(239,\s*68,\s*68/s + ); }); test('floating dialog drag stays on the compositor without forced style reads', () => {