- Add validation to check if Civitai API metadata contains recipe fields
- Fall back to EXIF extraction when API returns empty metadata (meta.meta=null)
- Improve error messages to distinguish between missing metadata and unsupported format
- Add _has_recipe_fields() helper method to validate metadata content
This fixes import failures for Civitai images where the API returns
metadata wrapper but no actual generation parameters (e.g., images
edited in Photoshop that lost their original generation metadata)
- Rename nodes to 'Checkpoint Loader (LoraManager)' and 'Unet Loader (LoraManager)'\n- Use os.sep for relative path formatting in model COMBO inputs\n- Update path matching to be robust across OS separators\n- Update docstrings and comments
Refactor _prepare_checkpoint_paths() to return a tuple instead of having
side effects on instance variables. This prevents extra unet paths from
being incorrectly classified as checkpoints when processing extra paths.
- Changed return type from List[str] to Tuple[List[str], List[str], List[str]]
(all_paths, checkpoint_roots, unet_roots)
- Updated _init_checkpoint_paths() and _apply_library_paths() callers
- Fixed extra paths processing to properly isolate main and extra roots
- Updated test_checkpoint_path_overlap.py tests for new API
This ensures models in extra unet paths are correctly identified as
diffusion_model type and don't appear in checkpoints list.
Add file_path as a tie-breaker for all sort modes in ModelCache, BaseModelService, LoraService, and RecipeCache to ensure deterministic ordering when primary keys are identical. Resolves issue #859.
- Replace multiple consecutive spaces with single underscore for tag matching
(e.g., 'looking to the side' → 'looking_to_the_side')
- Support prefix/suffix matching for flexible multi-word autocomplete
(e.g., 'looking to the' → 'looking_to_the_side')
- Add comprehensive test coverage for multi-word scenarios
Test coverage:
- Multi-word exact match (Danbooru convention)
- Partial match with last token replacement
- Command mode with multi-word phrases
- Multiple consecutive spaces handling
- Backend LOG10 popularity weight validation
Fixes: 'looking to the side' input now correctly replaces with
'looking_to_the_side, ' (or 'looking to the side, ' with space replacement)
- Add LOG10(post_count) weighting to BM25 score for better relevance ranking
- Prioritize tag_name prefix matches above alias matches using CASE statement
- Remove frontend re-scoring logic to trust backend排序 results
- Fix pagination consistency: page N+1 scores <= page N minimum score
Key improvements:
- '1girl' (6M posts) now ranks #1 instead of #149 for search '1'
- tag_name prefix matches always appear before alias matches
- Popular tags rank higher than obscure ones with same prefix
- Consistent ordering across pagination boundaries
Test coverage:
- Add test_search_tag_name_prefix_match_priority
- Add test_search_ranks_popular_tags_higher
- Add test_search_pagination_ordering_consistency
- Add test_search_rank_score_includes_popularity_weight
- Update test data with 15 tags starting with '1'
Fixes issues with autocomplete dropdown showing inconsistent results
when scrolling through paginated search results.
- Delete examples/metadata/ directory and all example files
- Real metadata.json files in model roots are better examples
- Examples were artificial and could become outdated
- Maintenance burden outweighs benefit
- Remove 'Complete Examples' section from docs/metadata-json-schema.md
- Remove reference to example files in 'See Also' section
Rationale:
Users have access to real-world metadata.json files in their actual
model directories, which contain complete Civitai API responses with
authentic data structures (images arrays with prompts, files with hashes,
creator information, etc.). These are more valuable than simplified
artificial examples.
- Replace static imports of deprecated ComfyButton and ComfyButtonGroup with dynamic imports
- Only loads legacy API files when frontend version < 1.33.9 (backward compatibility path)
- Frontend >= 1.33.9 users no longer see deprecation warnings since legacy code is never loaded
- Preserves full backward compatibility for older ComfyUI frontend versions
- All existing tests pass (159 JS + 65 Vue tests)
- Create docs/metadata-json-schema.md with complete field reference
- All base fields for LoRA, Checkpoint, and Embedding models
- Complete civitai object structure with Used vs Stored field classification
- Model-level fields (allowCommercialUse, allowDerivatives, etc.)
- Creator fields (username, image)
- customImages structure with actual field names and types
- Field behavior categories (Auto-Updated, Set Once, User-Editable)
- Add .specs/metadata.schema.json for programmatic validation
- JSON Schema draft-07 format
- oneOf schemas for each model type
- Definitions for civitaiObject and usageTips
- Add example metadata files for each model type
- lora-civitai.json: LoRA with full Civitai data
- lora-custom.json: User-defined LoRA with trigger words
- lora-no-triggerwords.json: LoRA without trigger words
- checkpoint-civitai.json: Checkpoint from Civitai
- embedding-custom.json: Custom embedding
Key clarifications:
- modified: Import timestamp (Set Once, never changes after import)
- size: File size at import time (Set Once)
- base_model: Optional with actual values (SDXL 1.0, Flux.1 D, etc.)
- model_type: Used in metadata.json (not sub_type which is internal)
- allowCommercialUse: ["Image", "Video", "RentCivit", "Rent"]
- civitai.files/images: Marked as Used by Lora Manager
- User-editable fields clearly documented (model_name, tags, etc.)
- Add BatchImportService with concurrent execution using asyncio.gather
- Implement AdaptiveConcurrencyController with dynamic adjustment
- Add input validation for URLs and local paths
- Support duplicate detection via skip_duplicates parameter
- Add WebSocket progress broadcasting for real-time updates
- Create comprehensive unit tests for batch import functionality
- Update API handlers and route registrations
- Add i18n translation keys for batch import UI
- Update get_lora_info() to check both loras_roots and extra_loras_roots
- Add fallback logic to return trigger words even if path not in recognized roots
- Ensure Trigger Word Toggle node displays trigger words for LoRAs from extra folder paths
Fixes issue where LoRAs added from extra folder paths would not show their trigger words in connected Trigger Word Toggle nodes.
- Implement version detection using __COMFYUI_FRONTEND_VERSION__ and /system_stats API
- Add version parsing and comparison utilities
- Dynamically register extension based on frontend version
- Use actionBarButtons API for frontend >= 1.33.9
- Fallback to legacy ComfyButton approach for older versions
- Add comprehensive version detection tests
- Import and use escapeHtml and escapeAttribute in SidebarManager.js
- Escape data-path and title attributes in folder tree and breadcrumbs
- Use CSS.escape() for attribute selectors in updateTreeSelection
- Fixes issue #843 where folders with double quotes broke navigation
Add @wheel event listener to AutocompleteTextWidget textarea to enable canvas zoom when textarea has no scrollbar.
The onWheel handler:
- Forwards pinch-to-zoom (ctrl+wheel) to canvas
- Passes horizontal scroll to canvas
- When textarea has vertical scrollbar: lets textarea scroll
- When textarea has NO scrollbar: forwards to canvas for zoom
Behavior now matches ComfyUI built-in multiline widget.
Fixes#850
Change node-selector z-index from 1000 to var(--z-overlay) (2000)
to ensure the model selector UI appears above the recipe modal
when sending checkpoints to workflow with multiple targets.
Backend _relative_path_matches_tokens() removes extensions from paths
before matching (commit 43f6bfab). This fix ensures frontend also
removes extensions from search terms to avoid matching failures.
Fixes issue where send model to workflow would receive absolute
paths instead of relative paths because the API returned empty
results when searching with file extension.
- Add GET /api/lm/example-workflows endpoint to list available templates
- Add GET /api/lm/example-workflows/{filename} to retrieve specific workflow
- Add 'New Tab Template Workflow' setting in LoRA Manager settings
- Automatically apply 80% zoom level when loading template workflows
- Override workflow's saved view settings to prevent visual zoom flicker
The feature allows users to select a template workflow from example_workflows/
directory to load when creating new workflow tabs, with a hardcoded 0.8 zoom
level for better initial view experience.
Temporarily remove width constraints when measuring content to prevent
scrollWidth from being limited by narrow container. This fixes the issue
where dropdown width was incorrectly calculated as ~120px.
Also update test to match maxItems default value (100).
Add missing offset parameter to MockTagFTSIndex to support
pagination changes from commit a802a89.
- Update search() signature to include offset=0
- Implement pagination logic with offset/limit slicing
Remove .safetensors/.ckpt/.pt/.bin extensions from model names in autocomplete
suggestions to improve UX and search relevance:
Frontend (web/comfyui/autocomplete.js):
- Add _getDisplayText() helper to strip extensions from model paths
- Update _matchItem() to match against filename without extension
- Update render() and createItemElement() to display clean names
Backend (py/services/base_model_service.py):
- Add _remove_model_extension() helper method
- Update _relative_path_matches_tokens() to ignore extensions in matching
- Update _relative_path_sort_key() to sort based on names without extensions
Tests (tests/services/test_relative_path_search.py):
- Add tests to verify 's' and 'safe' queries don't match all .safetensors files
Fixes issue where typing 's' would match all .safetensors files and cluttered
suggestions with redundant extension names.
Move clear button from top-right to bottom-right to avoid
obscuring text content. Add hover visibility for cleaner UI.
Reserve bottom padding in textarea for button placement.
Implement search query variation generation to improve matching for multi-word tags:
- Generate multiple query forms: original, underscore (spaces->_), no-space, last token
- Execute up to 4 parallel queries with result merging and deduplication
- Add smart matching with symbol-insensitive comparison (blue hair matches blue_hair)
- Sort results with exact matches prioritized over partial matches
This allows users to type natural language queries like 'looking to the side' and
find tags like 'Looking_to_the_side' while maintaining backward compatibility
with continuous typing workflows.
- Add syncChanges() function to recipeApi.js for quick refresh without cache rebuild
- Implement dropdown menu UI in recipes page with quick refresh and full rebuild options
- Add initDropdowns() method to RecipeManager for dropdown interaction handling
- Update AGENTS.md with more precise instruction about running sync_translation_keys.py
- Integrate sync changes functionality as default refresh behavior
- Add missing translations for modelTypes, recipe refresh, and sync notifications
- Translate for all supported languages (zh-CN, zh-TW, ja, ko, fr, de, es, ru, he)
- Run sync_translation_keys.py to ensure key consistency
Refactor force_refresh path to use thread pool execution instead of blocking
the event loop shared with ComfyUI. Key changes:
- Fix 1: Route force_refresh through _initialize_recipe_cache_sync() in thread pool
- Fix 2: Add GIL release points (time.sleep(0)) every 100 files in sync loops
- Fix 3: Move RecipeCache.resort() to thread pool via run_in_executor
- Fix 4: Persist cache automatically after force_refresh
- Fix 5: Increase yield frequency in _enrich_cache_metadata (every recipe)
This eliminates the ~5 minute freeze when rebuilding 30K recipe cache.
Fixes performance issue where ComfyUI became unresponsive during recipe
scanning due to shared Python event loop blocking.
- Add scripts/update_supporters.py to generate supporter list from JSON
- Set up GitHub Action to auto-update README.md on supporters.json change
- Update README.md with placeholders and personalized gratitude message
- Add auto-scrolling functionality to supporters list with user interaction controls (pause on hover, manual scroll)
- Implement gradient overlays at top/bottom for credits-like appearance
- Style custom scrollbar with subtle hover effects for better UX
- Adjust padding and positioning to ensure all supporters remain visible during scroll
- Refactor StatisticsManager to return promises from initializeVisualizations and initializeLists
- Update fetchAndRenderList to use the fetchData wrapper for consistent mocking
- Update statistics dashboard test to include mock data for paginated model-usage-list endpoint
- Add get_model_usage_list API endpoint for paginated stats
- Replace static rendering with client-side infinite scroll logic
- Add scrollbars and max-height to model usage lists
- Add missing keys 'common.cancel', 'common.confirm', and 'sidebar.dragDrop.noDragState' to en.json
- Synchronize all locale files using sync_translation_keys.py
- Complete translations for zh-CN, zh-TW, ja, ru, de, fr, es, ko, and he
- Implement sidebar drag-and-drop folder creation with visual feedback and input validation
- Optimize MoveManager to use resetAndReload for consistent UI state after moving models
- Fix recursive visibility check for root folder in MoveManager
- Add CivitAI URL utility with optimization strategies for showcase and thumbnail modes
- Replace /original=true with /optimized=true for showcase videos to reduce bandwidth
- Remove redundant crossorigin and referrerpolicy attributes from video elements
- Use media type detection to apply appropriate optimization (image vs video)
- Integrate URL optimization into showcase rendering for improved loading times
- Add new endpoint POST /api/lm/{prefix}/set-preview-from-url to handle
remote image downloads server-side, avoiding CORS issues
- Use rewrite_preview_url() to download optimized smaller images (450px width)
- Use Downloader service for reliable downloads with retry logic and proxy support
- Update frontend to call new endpoint instead of fetching images in browser
fixes#837
- Set draggable=true on recipe card div elements to enable drag-and-drop functionality
- This allows users to drag recipe cards for reordering or other interactions
Fix showcase expansion to work with both left-click and middle-click (drag scroll).
Problem: The scroll-indicator click events were only bound when the carousel
was in expanded state. Initial collapsed state meant no click handlers were
attached, so clicking did nothing.
Solution:
- Extract scroll-indicator event binding into separate bindScrollIndicatorEvents()
- Call bindScrollIndicatorEvents() immediately when showcase loads, regardless
of collapsed state
- Separate handlers for left-click (click event) and middle-click (mousedown
event) to avoid double-triggering
Changes:
- Add bindScrollIndicatorEvents() function for early event binding
- Use click event for left mouse button (button 0)
- Use mousedown event for middle mouse button (button 1)
- Update loadExampleImages() to bind events immediately
- Update initShowcaseContent() to use the new function
- Add 'performance' marker to pytest.ini
- Add pytestmark to test_cache_performance.py
- Use -m 'not performance' by default in addopts
- Allows manual execution with 'pytest -m performance'
- Remove left padding from changelog content container
- Add consistent padding to all changelog items
- Simplify latest changelog item styling by removing redundant padding
- Maintain visual distinction for latest items with background and border
Delay DOM creation in LoadingManager constructor to first use time,
ensuring window.i18n is ready before translate() is called.
This eliminates the 'i18n not available' console warning during
module initialization while maintaining correct translations
for cancel button and loading status text.
- Add SupportersHandler in misc_handlers.py to serve /api/lm/supporters
- Register new endpoint in misc_route_registrar.py
- Remove supporters from page load template context in model_handlers.py
- Create supportersService.js for frontend data fetching
- Update Header.js to fetch supporters when support modal opens
- Modify support_modal.html to use client-side rendering
This change improves page load performance by loading supporters data
on-demand instead of during initial page render.
- Allow empty sha256 when hash_status is 'pending' in cache entry validator
- Add on-demand hash calculation during bulk metadata refresh for checkpoints
with pending hash status
- Add comprehensive tests for both fixes
Fixes issue where checkpoints in extra paths were not visible in UI and
not processed during bulk metadata refresh due to empty sha256.
- Fix config.py: save and restore main paths when processing extra folder paths to prevent
_prepare_checkpoint_paths from overwriting checkpoints_roots and unet_roots
- Fix lora_manager.py: apply library settings during initialization to load extra folder paths
in ComfyUI plugin mode
- Fix checkpoint_routes.py: merge checkpoints/unet roots with extra paths in API endpoints
- Add logging for extra folder paths
Fixes issue where extra folder paths were not recognized for checkpoints and unet models.
- Fix config.py: save and restore main paths when processing extra folder paths to prevent
_prepare_checkpoint_paths from overwriting checkpoints_roots and unet_roots
- Fix lora_manager.py: apply library settings during initialization to load extra folder paths
in ComfyUI plugin mode
- Fix checkpoint_routes.py: merge checkpoints/unet roots with extra paths in API endpoints
- Add logging for extra folder paths
Fixes issue where extra folder paths were not recognized for checkpoints and unet models.
Checkpoints are typically large (10GB+). This change delays SHA256
hash calculation until metadata fetch from Civitai is requested,
significantly improving initial scan performance.
- Add hash_status field to BaseModelMetadata
- CheckpointScanner skips hash during initial scan
- On-demand hash calculation during Civitai fetch
- Background bulk hash calculation support
Introduce extra_folder_paths feature to allow users to add additional
model roots that are managed by LoRA Manager but not shared with ComfyUI.
Changes:
- Add extra_folder_paths support in SettingsManager (stored per library)
- Add extra path attributes in Config class (extra_loras_roots, etc.)
- Merge folder_paths with extra_folder_paths when applying library settings
- Update LoraScanner, CheckpointScanner, EmbeddingScanner to include
extra paths in their model roots
- Add comprehensive tests for the new functionality
This enables users to manage models from additional directories without
modifying ComfyUI's model folder configuration.
When searching in settings, the view now automatically scrolls to the
first matching element after switching to the matching section.
- Modified performSearch() to track and scroll to first match
- Modified highlightSearchMatches() to return the first highlight element
- Uses requestAnimationFrame and scrollIntoView with block: 'center'
- Restructure settings.sections and settings.nav in en.json
- Restore translations for existing keys across all locales (de, es, fr, he, ja, ko, ru, zh-CN, zh-TW)
- Add translations for new keys: metadata, library
- Translate autoOrganize section titles
- Complete all TODO translations in settings.search
- Handle non-string hash values by converting to string before lower()
- Add try-except for strength conversion to handle invalid values like empty strings
- Fixes hypothesis test failures when random data generates unexpected types
- Add missing mocks for comfy.sd and comfy.utils modules in conftest.py
- Fix i18n translation keys: use .help instead of .description for tooltip keys
lora_stack stores relative paths (e.g., 'Illustrious/style/file.safetensors'),
but comfy.utils.load_torch_file requires absolute paths. Previously, when
loading LoRAs from lora_stack, the relative path was passed directly to the
low-level API, causing FileNotFoundError on Windows.
This fix extracts the lora name from the relative path and uses
get_lora_info_absolute() to resolve the full absolute path before passing
it to load_torch_file(). This maintains compatibility with the lora_stack
format while ensuring correct file loading across all platforms.
Fixes: FileNotFoundError for relative paths in LoraLoaderLM and LoraTextLoaderLM
when processing lora_stack input.
- Remove bottom margin from setting items and last-child override
- Add flex layout to setting-info for inline label and info icon alignment
- Replace label opacity with rgba color for better tooltip visibility
- Add info-icon styling with hover tooltips using data-tooltip attribute
- Move help text from separate divs to inline tooltips on labels and section headers
- Improve tooltip positioning with edge case handling for left-aligned icons
- Move Priority Tags setting from separate section to bottom of Download Path Templates
- Fix help link button position to be inline with label using flexbox layout
- Add CSS styles for .priority-tags-header-row and .priority-tags-header
- Fix metadata archive DB setting to use correct i18n keys (enableArchiveDb, etc.)
- Restore metadata archive status display and management buttons
- Fix proxy settings to use correct i18n keys (enableProxy, proxyType, proxyHost, etc.)
- Add missing help text for proxy settings
- Add SOCKS4 proxy option
- Add onblur/onkeydown handlers for proxy input fields
- Update locales for new nav items (organization, system, network)
- Increase modal width from 800px to 1000px to accommodate more content
- Change height from fixed 600px to dynamic calculation based on viewport height
- Maintain responsive constraints with max-width and max-height properties
- Remove fixed min-height from card-footer for adaptive sizing
- Increase model-name max-height to 5.6em (4 lines)
Enables full display of long custom-trained LoRA filenames
- Add get_lora_info_absolute() function to return absolute file paths
- Replace LoraLoader().load_lora() with comfy.utils.load_torch_file() +
comfy.sd.load_lora_for_models() to enable loading LoRAs from any path
- This allows LoRA Manager to load LoRAs from non-standard paths (multi-library support)
- Fixes#805
Initialize internalValue with default RandomizerConfig object instead of
undefined to prevent frontend from sending empty string to backend when
widget is first created.
This fixes the 'str' object has no attribute 'get' error that occurred
when running a newly created Lora Randomizer node before any user
interaction.
Fixes#4
- Add is_dirty flag to track if statistics have changed
- Only write stats file when data actually changes
- Add enable_usage_statistics setting in ComfyUI settings
- Skip backend requests when usage statistics is disabled
- Fix standalone mode compatibility for MetadataRegistry
Fixes#826
- Add overflow-wrap: anywhere to modal title for proper wrapping of hyphenated names
- Add tooltip to model cards showing full filename on hover
Fixes overlap issues with long filenames like s0r4B35G_Zibv3_Prodigy_ID_Version2_Final_00800
Instead of always using default paths, downloads from the model versions
tab now target the same directory as the current in-library version.
Falls back silently to default paths if the current version path cannot
be resolved.
Store textarea reference on container element to allow cloned widgets to access inputEl when promoted to subgraph nodes. This ensures both original and cloned widgets can properly get and set values through the shared DOM element.
Update the help text for 'Metadata Refresh Skip Paths' setting to explicitly
state that paths should be relative to the 'model root directory' instead of
just saying 'relative folder paths', which was ambiguous.
Updated translations:
- English (en)
- Chinese Simplified (zh-CN)
- Chinese Traditional (zh-TW)
- Japanese (ja)
- Korean (ko)
- Russian (ru)
- German (de)
- French (fr)
- Spanish (es)
- Hebrew (he)
Replace _SYNC_KEYS (37 keys) with _NO_SYNC_KEYS (5 keys) in SettingsHandler.
New settings automatically sync to frontend unless explicitly excluded.
Changes:
- SettingsHandler now syncs all settings except those in _NO_SYNC_KEYS
- Added keys() method to SettingsManager for iteration
- Updated tests to use new behavior
Benefits:
- No more missing keys when adding new settings
- Reduced maintenance burden
- Explicit exclusions for sensitive/internal settings only
Fixes: #86
Complete TODO translations from previous commit:
- Add translations for hideEarlyAccessUpdates setting
- Add translations for EA time formatting (endingSoon, hours, days)
- Add translations for EA badges and tooltips
- Translate to: de, es, fr, he, ja, ko, ru, zh-CN, zh-TW
Closes#815 translations
Add Early Access version support with filtering and improved UI:
Backend:
- Add is_early_access and early_access_ends_at fields to ModelVersionRecord
- Implement two-phase EA detection (bulk API + single API enrichment)
- Add hide_early_access_updates setting to filter EA updates
- Update has_update() and has_updates_bulk() to respect EA filter setting
- Add _enrich_early_access_details() for precise EA time fetching
- Fix setting propagation through base_model_service and model_update_service
Frontend:
- Add smart relative time display for EA (in Xh, in Xd, or date)
- Replace EA label with clock icon in metadata (fa-clock)
- Show Download button with bolt icon for EA versions (fa-bolt)
- Change EA badge color to #F59F00 (CivitAI Buzz theme)
- Fix toggle UI for hide_early_access_updates setting
- Add translation keys for EA time formatting
Tests:
- Update all tests to pass with new EA functionality
- Add test coverage for EA filtering logic
Closes#815
- Backend: Support limit=0 to return all tags in top-tags API
- Frontend: Remove tags limit setting and fetch all tags by default
- UI: Implement virtual scrolling in TagsModal for performance
- Initial display 200 tags, load more on scroll
- Show all results when searching
- Remove lora_pool_tags_limit setting to simplify UX
Fixes#819
Fix KeyError when 'hashes', 'name', or 'model' fields are missing from
Civitai API responses. Use .get() with defaults instead of direct dict
access in:
- LoraMetadata.from_civitai_info()
- CheckpointMetadata.from_civitai_info()
- EmbeddingMetadata.from_civitai_info()
- RecipeScanner._get_hash_from_civitai()
- DownloadManager._process_download()
Fixes#820
Previously, long LoRA filenames were truncated from the right with ellipsis,
which hid important checkpoint step numbers (e.g., -00800, -01000) that users
need to distinguish between different training checkpoints.
Changes:
- Replace single-line truncation with multi-line display (max 3 lines)
- Add line-height and word-break properties for better readability
- Use -webkit-line-clamp to gracefully handle extremely long names
This ensures the step number suffix is always visible in the tooltip.