The trigger words widget was created with node.addWidget(\"text\", ...) which
uses a simple single-line input even with multiline: true. In ComfyUI this
does not render multiple lines properly, making trigger words invisible.
Switch to ComfyWidgets[\"STRING\"](..., { multiline: true }) which creates a
proper <textarea> element that can display multi-line text.
Also fix preview area overlapping widget area by reserving ~250px for widgets
and capping preview height so it starts below the trigger words textarea.
The preview image area was fixed at 200px height regardless of node size.
When users dragged the node corner to enlarge it, the preview stayed the
same size while the space above it grew, leaving an awkward gap.
Change getPreviewArea() to compute a dynamic height:
- 35% of node height (scales with resize)
- clamped between 80px min and 400px max
Width already scaled with node.size[0]; now height does too.
Also update the minimum node height from a fixed PREVIEW_HEIGHT-based
value to a conservative estimate (260px) that fits widgets + min preview.
When the user switched from a LoRA with cached data to one without,
the button stayed as \"Refresh\" and stale trigger words / preview images
remained visible. This was because node-level civitai state was not
cleared on lora_name change.
Hook into the lora_name widget's callback to detect changes and reset:
- civitai_cache (null)
- civitai_image_index (0)
- civitai_nsfw_unblurred ({})
- trigger words text (empty)
Then update the button label back to \"Fetch Civitai Info\" and redraw.
download_image() wrote directly to dest_path without ensuring the parent
directory exists. This caused FileNotFoundError (caught by OSError handler)
when the per-hash image subdirectory under cache/civitai/ had not been
created yet, silently failing all image downloads.
Add os.makedirs(..., exist_ok=True) before open() to fix this.
Add config.py module to read user settings from config.json (not tracked
by git). Proxy URL and request timeout are no longer hard-coded.
Configuration priority:
1. config.json 'civitai.proxy' — explicit user setting
2. HTTP_PROXY/HTTPS_PROXY environment variables — fallback
3. No proxy — if neither is set
Files added:
- config.py — configuration loader with get_proxy_url() and get_timeout()
- config.json.example — template showing available options
Files modified:
- civitai_client.py — uses config.py instead of hard-coded values;
proxy is passed explicitly to each request
- .gitignore — ignores config.json so user settings survive updates
Users in regions where Civitai is blocked can copy config.json.example
to config.json and adjust proxy settings without touching source code.
Connections through HTTP proxies (e.g. mihomo/clash in mainland China)
often need more than 10 seconds to establish. Increase timeout to 30s
to prevent TimeoutError when fetching Civitai metadata or images.
- Enable trust_env=True in aiohttp ClientSession so Civitai API requests
respect HTTP_PROXY/HTTPS_PROXY environment variables (required in regions
where Civitai is blocked, e.g. mainland China behind mihomo/clash).
- Add _err_msg() helper to handle exceptions with empty str() (e.g.
TimeoutError, certain aiohttp errors) by falling back to repr().
- Replace all bare str(e) in error responses with _err_msg(e) so users
always see a meaningful error description.
- Add requirements.txt for ComfyUI Manager dependency installation
- Add CLAUDE.md with project architecture documentation
- Expand .gitignore to cover Python artifacts, test caches, IDE configs,
virtual environments, logs, coverage, and OS files
- Cache manager with JSON read/write, TTL, corruption handling
- Civitai client with SHA256 hash (sync+async) and API fetch
- KaiCivitaiLoRALoader node with trigger words and JSON output
- Node registration, HTTP routes, and secure image serving
- Frontend extension with image preview, pagination, and NSFW blur
- 49 unit tests covering all modules