- Simplify pytest coverage command by consolidating --cov flags - Remove redundant JSON coverage report - Reorganize frontend section into "Frontend Development (Standalone Web UI)" and "Vue Widget Development" - Add npm commands for Vue widget development (dev, build, typecheck, tests) - Consolidate Python code style sections (imports, formatting, naming, error handling, async) - Update architecture overview with clearer service descriptions - Fix truncated line in API endpoints note - Improve readability and remove redundant information
7.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
ComfyUI LoRA Manager is a comprehensive LoRA management system for ComfyUI that combines a Python backend with browser-based widgets. It provides model organization, downloading from CivitAI/CivArchive, recipe management, and one-click workflow integration.
Development Commands
Backend
pip install -r requirements.txt
pip install -r requirements-dev.txt
# Run standalone server (port 8188 by default)
python standalone.py --port 8188
# Run all backend tests
pytest
# Run specific test file or function
pytest tests/test_recipes.py
pytest tests/test_recipes.py::test_function_name
# Run backend tests with coverage
COVERAGE_FILE=coverage/backend/.coverage pytest \
--cov=py \
--cov=standalone \
--cov-report=term-missing \
--cov-report=html:coverage/backend/html \
--cov-report=xml:coverage/backend/coverage.xml \
--cov-report=json:coverage/backend/coverage.json
Frontend
There are three test suites run by npm test: vanilla JS tests (vitest at root) and Vue widget tests (vue-widgets/ vitest).
npm install
cd vue-widgets && npm install && cd ..
# Run all frontend tests (JS + Vue)
npm test
# Run only vanilla JS tests
npm run test:js
# Run only Vue widget tests
npm run test:vue
# Watch mode (JS tests only)
npm run test:watch
# Frontend coverage
npm run test:coverage
# Build Vue widgets (output to web/comfyui/vue-widgets/)
cd vue-widgets && npm run build
# Vue widget dev mode (watch + rebuild)
cd vue-widgets && npm run dev
# Typecheck Vue widgets
cd vue-widgets && npm run typecheck
Localization
# Sync translation keys after UI string updates
python scripts/sync_translation_keys.py
Locale files are in locales/ (en, zh-CN, zh-TW, ja, ko, fr, de, es, ru, he).
Architecture
Dual Mode Operation
The system runs in two modes:
- ComfyUI plugin mode: Integrates with ComfyUI's PromptServer, uses
folder_pathsfor model discovery - Standalone mode:
standalone.pymocks ComfyUI dependencies, reads paths fromsettings.json - Detection:
os.environ.get("LORA_MANAGER_STANDALONE", "0") == "1"
Backend (Python)
Entry points:
__init__.py— ComfyUI plugin entry: registers nodes viaNODE_CLASS_MAPPINGS, setsWEB_DIRECTORY, callsLoraManager.add_routes()standalone.py— Standalone server: mocksfolder_pathsand node modules, starts aiohttp serverpy/lora_manager.py— MainLoraManagerclass that registers all HTTP routes
Service layer (py/services/):
ServiceRegistrysingleton for dependency injection; services followget_instance()singleton patternBaseModelServiceabstract base →LoraService,CheckpointService,EmbeddingServiceModelScannerbase →LoraScanner,CheckpointScanner,EmbeddingScannerfor file discovery with hash-based deduplicationPersistentModelCache— SQLite-based metadata cacheMetadataSyncService— Background sync from CivitAI/CivArchive APIsSettingsManager— Settings with schema migration supportWebSocketManager— Real-time progress broadcastingModelServiceFactory— Creates the right service for each model type- Use cases in
py/services/use_cases/orchestrate complex business logic (auto-organize, bulk refresh, downloads)
Routes (py/routes/):
- Route registrars organize endpoints by domain:
ModelRouteRegistrar,RecipeRouteRegistrar, etc. - Request handlers in
py/routes/handlers/implement route logic - API endpoints follow
/loras/*,/checkpoints/*,/embeddings/*patterns - All routes use aiohttp, return
web.json_responseorweb.Response
Recipe system (py/recipes/):
base.py— Recipe metadata structureenrichment.py— Enriches recipes with model metadataparsers/— Parsers for PNG metadata, JSON, and workflow formats
Custom nodes (py/nodes/):
- Each node class has a
NAMEclass attribute used as key inNODE_CLASS_MAPPINGS - Standard ComfyUI node pattern:
INPUT_TYPES()classmethod,RETURN_TYPES,FUNCTION - All nodes registered in
__init__.py
Configuration (py/config.py):
- Manages folder paths for models, handles symlink mappings
- Auto-saves paths to settings.json in ComfyUI mode
Frontend — Two Distinct UI Systems
1. Standalone Manager Web UI
- Location:
static/(JS/CSS) andtemplates/(HTML) - Tech: Vanilla JS + CSS, served by standalone server
- Structure:
static/js/core.js(shared),loras.js,checkpoints.js,embeddings.js,recipes.js,statistics.js - Tests:
tests/frontend/**/*.test.js(vitest + jsdom)
2. ComfyUI Custom Node Widgets
- Vanilla JS widgets:
web/comfyui/*.js— ES modules extending ComfyUI's LiteGraph UIloras_widget.js/loras_widget_events.js— Main LoRA selection widgetautocomplete.js— Trigger word and embedding autocompletepreview_tooltip.js— Model card preview tooltipstop_menu_extension.js— "Launch LoRA Manager" menu itemutils.js— Shared utilities and API helpers- Widget styling in
web/comfyui/lm_styles.css(NOTstatic/css/)
- Vue widgets:
vue-widgets/src/→ built toweb/comfyui/vue-widgets/- Vue 3 + TypeScript + PrimeVue + vue-i18n
- Vite build with CSS-injected-by-JS plugin
- Components:
LoraPoolWidget,LoraRandomizerWidget,LoraCyclerWidget,AutocompleteTextWidget - Auto-built on ComfyUI startup via
py/vue_widget_builder.py - Tests:
vue-widgets/tests/**/*.test.ts(vitest)
Widget registration pattern:
- Widgets use
app.registerExtension()andgetCustomWidgetshooks node.addDOMWidget(name, type, element, options)embeds HTML in LiteGraph nodes- See
docs/dom_widget_dev_guide.mdfor DOMWidget development guide
Code Style
Python:
- PEP 8, 4-space indentation, English comments only
- Use
from __future__ import annotationsfor forward references - Use
TYPE_CHECKINGguard for type-checking-only imports - Loggers via
logging.getLogger(__name__) - Custom exceptions in
py/services/errors.py - Async patterns:
async deffor I/O,@pytest.mark.asynciofor async tests - Singleton pattern with class-level
asyncio.Lock(seeModelScanner.get_instance())
JavaScript:
- ES modules, camelCase functions/variables, PascalCase classes
- Widget files use
*_widget.jssuffix - Prefer vanilla JS for
web/comfyui/widgets, avoid framework dependencies (except Vue widgets)
Testing
Backend (pytest):
- Config in
pytest.ini:--import-mode=importlib, testpaths=tests - Fixtures in
tests/conftest.pyhandle ComfyUI dependency mocking - Markers:
@pytest.mark.asyncio,@pytest.mark.no_settings_dir_isolation - Uses
tmp_path_factoryfor directory isolation
Frontend (vitest):
- Vanilla JS tests:
tests/frontend/**/*.test.jswith jsdom - Vue widget tests:
vue-widgets/tests/**/*.test.tswith jsdom + @vue/test-utils - Setup in
tests/frontend/setup.js
Key Integration Points
- Settings: Stored in user directory (via
platformdirs) or portable mode ("use_portable_settings": true) - CivitAI/CivArchive: API clients for metadata sync and model downloads; CivitAI API key in settings
- Symlink handling: Config scans symlinks to map virtual→physical paths; fingerprinting prevents redundant rescans
- WebSocket: Broadcasts real-time progress for downloads, scans, and metadata sync
- Model scanning flow: Walk folders → compute hashes → deduplicate → extract safetensors metadata → cache in SQLite → background CivitAI sync → WebSocket broadcast