Files
ComfyUI-Lora-Manager/vue-widgets/src/components/lora-pool/LoraPoolSummaryView.vue
Will Miao 76c15105e6 feat(lora-pool): add regex include/exclude name pattern filtering (#839)
Add name pattern filtering to LoRA Pool node allowing users to filter
LoRAs by filename or model name using either plain text or regex patterns.

Features:
- Include patterns: only show LoRAs matching at least one pattern
- Exclude patterns: exclude LoRAs matching any pattern
- Regex toggle: switch between substring and regex matching
- Case-insensitive matching for both modes
- Invalid regex automatically falls back to substring matching
- Filters apply to both file_name and model_name fields

Backend:
- Update LoraPoolLM._default_config() with namePatterns structure
- Add name pattern filtering to _apply_pool_filters() and _apply_specific_filters()
- Add API parameter parsing for name_pattern_include/exclude/use_regex
- Update LoraPoolConfig type with namePatterns field

Frontend:
- Add NamePatternsSection.vue component with pattern input UI
- Update useLoraPoolState to manage pattern state and API integration
- Update LoraPoolSummaryView to display NamePatternsSection
- Increase LORA_POOL_WIDGET_MIN_HEIGHT to accommodate new UI

Tests:
- Add 7 test cases covering text/regex include, exclude, combined
  filtering, model name fallback, and invalid regex handling

Closes #839
2026-03-19 17:15:05 +08:00

116 lines
3.3 KiB
Vue

<template>
<div class="summary-view">
<!-- Filter sections -->
<div class="summary-view__filters">
<BaseModelSection
:selected="selectedBaseModels"
:models="availableBaseModels"
@edit="$emit('open-modal', 'baseModels')"
/>
<TagsSection
:include-tags="includeTags"
:exclude-tags="excludeTags"
@edit-include="$emit('open-modal', 'includeTags')"
@edit-exclude="$emit('open-modal', 'excludeTags')"
/>
<FoldersSection
:include-folders="includeFolders"
:exclude-folders="excludeFolders"
@update:include-folders="$emit('update:includeFolders', $event)"
@update:exclude-folders="$emit('update:excludeFolders', $event)"
@edit-include="$emit('open-modal', 'includeFolders')"
@edit-exclude="$emit('open-modal', 'excludeFolders')"
/>
<NamePatternsSection
:include-patterns="includePatterns"
:exclude-patterns="excludePatterns"
:use-regex="useRegex"
@update:include-patterns="$emit('update:includePatterns', $event)"
@update:exclude-patterns="$emit('update:excludePatterns', $event)"
@update:use-regex="$emit('update:useRegex', $event)"
/>
<LicenseSection
:no-credit-required="noCreditRequired"
:allow-selling="allowSelling"
@update:no-credit-required="$emit('update:noCreditRequired', $event)"
@update:allow-selling="$emit('update:allowSelling', $event)"
/>
</div>
<!-- Preview -->
<LoraPoolPreview
:items="previewItems"
:match-count="matchCount"
:is-loading="isLoading"
@refresh="$emit('refresh')"
/>
</div>
</template>
<script setup lang="ts">
import BaseModelSection from './sections/BaseModelSection.vue'
import TagsSection from './sections/TagsSection.vue'
import FoldersSection from './sections/FoldersSection.vue'
import NamePatternsSection from './sections/NamePatternsSection.vue'
import LicenseSection from './sections/LicenseSection.vue'
import LoraPoolPreview from './LoraPoolPreview.vue'
import type { BaseModelOption, LoraItem } from '../../composables/types'
import type { ModalType } from '../../composables/useModalState'
defineProps<{
// Base models
selectedBaseModels: string[]
availableBaseModels: BaseModelOption[]
// Tags
includeTags: string[]
excludeTags: string[]
// Folders
includeFolders: string[]
excludeFolders: string[]
// Name patterns
includePatterns: string[]
excludePatterns: string[]
useRegex: boolean
// License
noCreditRequired: boolean
allowSelling: boolean
// Preview
previewItems: LoraItem[]
matchCount: number
isLoading: boolean
}>()
defineEmits<{
'open-modal': [modal: ModalType]
'update:includeFolders': [value: string[]]
'update:excludeFolders': [value: string[]]
'update:includePatterns': [value: string[]]
'update:excludePatterns': [value: string[]]
'update:useRegex': [value: boolean]
'update:noCreditRequired': [value: boolean]
'update:allowSelling': [value: boolean]
refresh: []
}>()
</script>
<style scoped>
.summary-view {
display: flex;
flex-direction: column;
height: 100%;
}
.summary-view__filters {
flex: 1;
overflow-y: auto;
padding-right: 4px;
margin-right: -4px;
/* Allow flex item to shrink below content size */
min-height: 0;
}
</style>