The codebase is organized into four layers, each with a clear responsibility:
- Main process entry (
src/main/index.ts) — Window lifecycle only. Delegates all IPC to a separate module. src/main/lib/— Shared main-process utilities (HTTP helpers, process management, etc.). IPC registration lives insrc/main/lib/ipc/, split across handler modules (registerSessionHandlers.ts,registerInstallationHandlers.ts, etc.).src/main/sources/— One module per install method, plus a registry (index.ts). Each source is self-contained: it defines its install form, detail view, actions, and data shape.src/renderer/src/— Vue 3 renderer. Views are generic and data-driven — they render whatever sources describe. Shared logic lives incomposables/,lib/, andcomponents/.
Supporting files (data store in src/main/installations.ts and src/main/settings.ts, preload bridge in src/preload/) remain thin pass-throughs with no business logic.
Each source module (src/main/sources/<name>.ts or src/main/sources/<name>/index.ts) defines:
id,label— identity (used by ipc.js to injectsourceId/sourceLabelautomatically; sources should not repeat these inbuildInstallation)fields— what the new-install form rendersgetFieldOptions(fieldId, selections)— populates each fieldbuildInstallation(selections)— returns source-specific data to persistgetListActions(installation)— defines which action buttons appear on the list card (same schema as detail actions:id,label,style,enabled)getDetailSections(installation)— defines what the detail view shows (info fields, actions)probeInstallation(dirPath)(optional) — examines a directory and returns source-specific metadata if it recognizes the contents (e.g., portable checks forpython_embeded, git checks for.git). Returnsnullif unrecognized. Used by "Track Existing" to auto-detect source type.getSettingsSections(settings)(optional) — defines settings fields for this source. Fields declaretype(path,number),id(settings key), and currentvalue. The renderer builds the form generically.handleAction(actionId, installation)— executes source-specific actionsgetLaunchCommand(installation)— returns{ cmd, args, cwd, port }describing how to start this ComfyUI installation, ornullif launch is not supported. The launcher uses this to spawn the process, poll the port, then open an app window.install(installation, tools)(optional) — performs the actual installation (download, extract, etc.). Receives shared tools{ sendProgress, download, cache, extract }from ipc.js rather than importing lib modules directly, keeping sources decoupled from infrastructure.
The renderer never contains source-specific knowledge. If it needs to behave differently per source, that behavior must be declared in the source's data (see principle 3). Additional sources beyond the three described in Install Methods below (e.g., remote, cloud, desktop) follow the same interface pattern.
Fields support multiple types, each handled generically by the renderer:
type: "select"— dropdown, auto-cascades to load the next field on change.type: "text"— text input with optionaldefaultValue. Does not auto-cascade. Can declareaction: { label }to render a button that triggers downstream field loading when clicked. Errors from downstream API calls display beneath the text field.
src/main/index.tsdoes not register IPC handlers —src/main/lib/ipc/does.src/main/lib/ipc/handles IPC only. It may referenceBrowserWindowwhen an IPC handler requires a parent window (e.g., native dialogs), but does not manage window lifecycle.- Each renderer view is its own file under
src/renderer/src/. - Shared utilities (
fetch.ts,util.ts, composables) are extracted, not duplicated.
The renderer should not hardcode if (actionId === "remove") or similar checks. Instead, sources declare behavior via metadata:
- Actions declare
confirm: { title, message }to trigger a confirmation modal. - Actions declare
style(primary,danger,default) for visual treatment. - Actions declare
enabledto control availability. - Actions declare
showProgress: trueandprogressTitleto route through the progress view for long-running operations.
Detail section fields support editable: true with an id — the renderer shows an input and auto-saves changes to the installation data via update-installation IPC.
Any new behavioral hint should follow this pattern: add a property to the action/section schema, handle it generically in the renderer.
Never use alert() or other native OS dialogs for user-facing messages. Use modal.alert() and modal.confirm() from the renderer's composables/lib so the experience stays consistent and themed.
sourceId/sourceLabelinjection is done bysrc/main/lib/ipc/, not by each source.- The
removeaction is handled centrally insrc/main/lib/ipc/since it's a generic CRUD operation. - HTTP fetching is in
lib/fetch.ts, not in individual sources.
This project aims to eventually replace Comfy-Org/desktop. Refer to that repo for prior art on features, conventions, and assets. Icons/logos in assets/ are sourced from desktop/assets/UI/.
Three source modules provide different installation strategies, each targeting different user needs.
Downloads Comfy-Org's official .7z portable release, which bundles Python embedded + pre-installed wheels + ComfyUI source in a single archive. Download → extract → run.
- Platforms: Windows (NVIDIA, AMD)
- Pros: Zero network dependency after initial download; uses official builds
- Cons: Windows only; monolithic archive means re-downloading everything for updates
- Status: Implemented
Our own pre-built environment archives. Each archive contains a relocatable Python runtime (from python-build-standalone) with all GPU-specific wheels (PyTorch, etc.) pre-installed. ComfyUI source is cloned via git separately. The result is a single download + extract for the environment, plus a git clone — no pip/uv/network-dependent package installation at install time.
- Platforms: Windows (NVIDIA, AMD, Intel), macOS (Apple Silicon), Linux (NVIDIA, AMD, Intel)
- Pros: Cross-platform portable install; no pip failures; environment and source are independent; source can be updated via git without re-downloading the environment
- Cons: Requires us to build and host environment archives; larger hosting footprint
standalone-install/
├── python/ # Relocatable Python from python-build-standalone
│ ├── bin/ or python.exe # Platform-specific Python binary
│ └── lib/site-packages/ # Pre-installed wheels (torch, etc.)
├── ComfyUI/ # Cloned from GitHub
│ ├── main.py
│ └── ...
└── .comfyui-desktop-2 # Marker file
Each archive is built per platform+GPU combination and hosted on GitHub Releases or a CDN.
Build steps (to be implemented as GitHub Actions):
- Download python-build-standalone release for the target platform
- Create a virtual environment or install directly into the standalone Python's site-packages
pip install(oruv pip install) the correct GPU-specific PyTorch wheels + ComfyUI requirements- Strip unnecessary files to reduce size (e.g.,
torch/lib/dnnl.lib, test directories,__pycache__) - Package as
.tar.zst(Linux/macOS) or.7z(Windows) for best compression - Upload as release artifacts
| Archive ID | OS | GPU | Python | PyTorch Index |
|---|---|---|---|---|
win-nvidia-cu130 |
Windows x64 | NVIDIA (CUDA 13.0) | 3.12 | cu130 |
win-nvidia-cu128 |
Windows x64 | NVIDIA (CUDA 12.8) | 3.12 | cu128 |
win-nvidia-cu126 |
Windows x64 | NVIDIA (CUDA 12.6) | 3.12 | cu126 |
win-intel-xpu |
Windows x64 | Intel Arc (XPU) | 3.12 | xpu |
win-amd |
Windows x64 | AMD (CPU baseline) | 3.12 | cpu |
win-cpu |
Windows x64 | CPU fallback | 3.12 | cpu |
mac-mps |
macOS arm64 | Apple Silicon (MPS) | 3.12 | default (MPS built-in) |
linux-nvidia-cu130 |
Linux x64 | NVIDIA (CUDA 13.0) | 3.12 | cu130 |
linux-nvidia-cu128 |
Linux x64 | NVIDIA (CUDA 12.8) | 3.12 | cu128 |
linux-nvidia-cu126 |
Linux x64 | NVIDIA (CUDA 12.6) | 3.12 | cu126 |
linux-intel-xpu |
Linux x64 | Intel Arc (XPU) | 3.12 | xpu |
linux-amd |
Linux x64 | AMD (ROCm 6.2.4) | 3.12 | rocm6.2.4 |
linux-cpu |
Linux x64 | CPU fallback | 3.12 | cpu |
detectGPU()+process.platform→ select the correct archive ID- Download the environment archive (with progress reporting)
- Extract to install directory
git clone --depth 1ComfyUI into theComfyUI/subdirectory- Write
.comfyui-desktop-2marker file
python/bin/python -s ComfyUI/main.py [launchArgs]
The -s flag prevents system site-packages from interfering. The standalone Python's own site-packages contain all dependencies.
- Hosting: GitHub Releases on the ComfyUI-Desktop-2.0-Beta repo (2GB per asset limit; archives should fit)
- Archive format:
.7zon Windows (best compression for executables via LZMA2),.tar.gzon Linux/macOS - Python-build-standalone variant:
install_only(smaller, includes pip via ensurepip, no build tools) - Git requirement:
pygit2is included in the environment; the launcher can use it for cloning without requiring system git - Size optimization: Strip
__pycache__, test directories, and large unused torch libs (dnnl.lib, libprotoc.lib, libprotobuf.lib). Do NOT strip.dist-info— these are needed byimportlib.metadatafor package dependency checks at runtime. - CI workflow:
.github/workflows/build-standalone-env.yml— manually dispatched, builds all 7 platform+GPU variants in parallel, uploads to a tagged GitHub Release
- Versioning: How to version/tag archives so the launcher knows which version is installed and when updates are available
- Archive sizes: Need to verify that CUDA-heavy archives (win-nvidia, linux-nvidia) fit within the 2GB GitHub Release asset limit after compression. If not, split into environment + wheels, or use a CDN.
- Windows AMD: ROCm on Windows is experimental. Currently building with CPU wheels; users can upgrade to ROCm nightly post-install. Revisit when official ROCm Windows wheels stabilize.
Clones ComfyUI from a Git repository. Intended for users who want fine-grained control over branches, commits, and dependencies.
- Platforms: All (wherever Python + git are available)
- Pros: Always up-to-date; user picks exact branch/commit; lightweight initial download for the launcher itself
- Cons: Network-dependent for both install and updates
- Status: Implemented
- Clone the selected repo/branch/commit from GitHub (using logged processes)
- Detect existing venvs via
findVenv - Launch using the detected venv's Python
See Future Enhancements below.
When reviewing code for modularity, check:
- Is any source-specific logic in the renderer? Move it to the source module's data/metadata.
- Is any file doing two unrelated things? Split it.
- Is the same value defined in two places? Derive it from a single source of truth.
- Is a utility duplicated across modules? Extract to
lib/. - Does a renderer view contain hardcoded conditionals for specific action/source IDs? Replace with a data-driven pattern (add a property to the schema).
- Does a source repeat information already available from its own definition? Have the framework (
src/main/lib/ipc/) inject it. - Can a new source be added by only creating a file in
src/main/sources/and registering it insrc/main/sources/index.ts? If not, something is coupled.
The synchronous writeFileSafe in lib/safe-file.ts cannot safely retry on
Windows EPERM/EACCES rename errors (retry would require a busy-wait that
blocks the event loop). The async variant writeFileSafeAsync handles this
with proper setTimeout-based retries.
Callers that should be migrated to writeFileSafeAsync:
src/main/settings.ts—set()functionsrc/main/lib/release-cache.ts—save()functionsrc/main/lib/models.ts—ensureModelPathsConfig()src/main/lib/fetch.ts—fetchJSON()cache write
Currently the sync version relies on readFileSafe's .bak fallback to
recover from a failed rename, which is adequate but not ideal.