Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155S2Ye8BRUakV7GHAsmYWu
8.5 KiB
How FunPack is built
Read this first. The other guides assume it.
The idea
FunPack is a browser app (/funpack/) on top of ComfyUI. Everything it does is a module that announces itself. Core keeps no list of features, so adding a feature is adding a folder, and removing one is deleting it.
The same idea runs on both sides:
| Side | Things that announce themselves | Where they announce |
|---|---|---|
| Backend (Python) | modules | constants in modules/<group>/<name>/__init__.py |
| App (JavaScript) | features | export default { id, mount, setup } listed in app/ui/modules.js |
| App kit | UI elements | define(group, variant, factory) in app/ui/composer/elements/ |
Anything that is announced can be absent. A module that fails to import, fails validation, or breaks during a run is left out, the Log says why, and everything else carries on. The UI follows the same rule: a feature that cannot load is not drawn at all (no grey button, no warning chip).
The layers
browser app/ui/ shell + kit + features (plain ES modules, no build step)
|
aiohttp core/routes.py routes mounted on ComfyUI's own server under /funpack/
|
core/ Python finds modules, validates them, builds the ComfyUI prompt
|
modules/ Python features, grouped by domain
|
ComfyUI nodes + sampler executes the prompt
- core/ knows how to serve the app, load modules and build a ComfyUI request. It never names a feature.
- modules/ is every feature: loaders, sampling techniques, model support, output, learning from ratings.
- app/ui/shell/ is the app's own plumbing: the page frame, project and pipeline state, the feature loader, the API client.
- app/ui/composer/ is the UI kit (called the Composer): tokens, internals, elements. It is the only code that styles anything.
- app/ui/features/ is every screen feature: timeline, media bin, preview, settings sections, Generate.
How a setting travels
- A module declares
SETTINGS(see Writing a module). GET /funpack/api/modulessends the browser the validated declarations, ordered by stage and relations, with the model's traits applied.- Settings > Engine (and the Sampler window) draw one row per setting. Nothing is hand-built per module.
- An edit is saved with the project. The values travel as one JSON object,
{module_id: {key: value}}, in thesettingsinput of the node a module named as itssettings_sink(todayFunPack Modifier Settings). - At run time
FunPack Modifier Settingschecks every value against its declaration andFunPack Load Modifierscalls each enabled module'sinstall(patcher, values, key)in order.
The screen is the truth: Generate reads the live UI state, and a module never reads the UI.
The pipeline is data
The pipeline is a list of slots (core/graph.py): each names a node and what feeds it. Nothing is a fixed chain, so a slot can be pointed at another node or removed, and the server refuses what would not build and says why.
- A slot's
rolesput one of its inputs on the app's surface (the prompt box, width and height, steps, seed). A role'satnames the place (generation.prompt,project.video, ...). - A slot's
groupis how the pipeline reads to a person (the cards in Models & Pipeline). - Model modules offer ready-made pipelines through
pipeline_presets.
Failure model
The rules below are enforced by code, not by habit. Each one names where.
| What goes wrong | What happens | Where |
|---|---|---|
| A module raises on import | Absent. One Warning in the Log. | core/registry.py |
| A declaration is invalid | Not loaded. The Log names the setting and the rule. | core/schema.py |
| Two modules share an id or a node id | The second is left out and said. | registry.add, core/nodes.py |
| Saved settings name a module or key that no longer exists | Skipped with one Log line. Old projects keep working. | schema.check_values, modifier nodes |
| A setting value is wrong | The run is refused, naming it (unless Full control is on). | schema.refuse_or_warn |
install raises |
That module is off for the run and quarantined. | core/control.py |
| A hook raises on step 3 of 30 | The hook is dropped for the rest of the run, the run continues. | core/patching.py |
| A module was quarantined | It stays off until its code changes (and ComfyUI restarts) or the person turns it on in Settings > Modules. | core/control.py |
| A relation cannot be satisfied (cycle) | The modules on the cycle are left out, and said. | core/relations.py |
| A model lacks a trait a module requires | The module is not offered for that model. | core/traits.py |
Out-of-memory errors and interrupts are not a module's fault and never quarantine it.
Independence
- A module reaches core only through
modules/_core.py(from ..._core import log, registry). A directimport coreworks in tests and breaks inside ComfyUI, so a test forbids it. - A module never imports another module when the file loads. It asks by capability:
registry.current().ask("name", ...). If the provider is missing or raises, the answer isNoneand the asking module copes. A test forbids the import. - A feature may not break the baseline, and the baseline does not depend on a feature on top of it.
- The same holds in the browser: a feature never imports another feature. It talks through
app(see Writing a UI feature).
Who may touch what
There are three trust levels in the app code. Know which one you are writing.
| You are writing | May style or build DOM? | Checked by |
|---|---|---|
| A backend module's settings (Python only) | No JavaScript at all. The kit draws the rows. | core/schema.py, core/tests/test_shipped_hints.py |
A UI feature in app/ui/features/ |
It builds with kit elements and reaches the page only through app (app.dom). No document, window, style, class or colour of its own. |
tools/kit_lint FEATURE_RULES, composer/tests/rule_features_use_the_kit.test.js |
The kit and the shell (app/ui/shell/) |
Yes. The kit is the only place that styles elements; the shell owns the page frame (frame.css) and is the other place that touches document. |
tools/kit_lint KIT_RULES, tests in composer/tests/ |
Be exact about what the lint is. tools/kit_lint is a discipline check, not a security boundary: it reads text, so it catches the accidental, not a determined bypass. Modules run only in Python and are never served to the browser; first-party modules are reviewed.
Where things live
| Path | What |
|---|---|
__init__.py |
ComfyUI entry point. Mounts the routes and hands ComfyUI the nodes modules announced. |
core/ |
Registry, schema, traits, relations, chain, control, patching, graph, routes, run, log. |
modules/_core.py |
The one place a module reaches core from. |
modules/<group>/<name>/ |
One module. __init__.py is the announcement. |
modules/<group>/*.py |
Helpers shared inside a group. Announce nothing. May not import a module. |
app/ui/boot.js |
Starts the frame, the core services, then loads features. Names no feature. |
app/ui/modules.js |
The one list of UI features. |
app/ui/shell/ |
Frame, mounts, project and pipeline state, API client, settings window. |
app/ui/composer/ |
The kit: tokens/, internals/, elements/, panel.js, tests/. |
app/ui/features/<name>/ |
One feature. |
app/ui/catalogue/ |
A page that shows every kit element and token, in both themes. |
app/assets/fonts/ |
Bundled fonts (Inter, Inter Tight, IBM Plex Mono, all OFL). |
tools/devserver.py |
Serves the app and the real routes without ComfyUI. |
tools/kit_lint/ |
The rule scanner. |
docs/ |
These documents. |
Python tests sit beside what they test (core/tests/, modules/<group>/<name>/tests/). JavaScript tests sit beside the code (*.test.js) or in composer/tests/ and shell/tests/.
Running it locally
- With ComfyUI: clone into
ComfyUI/custom_nodes,pip install -r requirements.txt, start ComfyUI, open/funpack/. - Without ComfyUI:
python tools/devserver.py 8199serves the app on the real routes. There is no queue behind it, so Generate says so. SetCOMFYUI_ROOTto a ComfyUI checkout, or most modules will be absent (they importcomfy). - The catalogue:
/funpack/app/ui/catalogue/index.html(tokens),elements.html(every element, both themes),hide.html(what happens when a module is broken),log.html.
Tests are covered in Testing and verification.