mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-09 07:00:06 +00:00
[feat] Add ownership filter to model browser (#7201)
## Summary Adds a dropdown filter to the model browser that allows users to filter assets by ownership (All, My models, Public models), based on the `is_immutable` property. ## Changes - **Filter UI**: Added ownership dropdown in [AssetFilterBar.vue](src/platform/assets/components/AssetFilterBar.vue#L30-L38) that only appears when user has uploaded models - **Filter Logic**: Implemented `filterByOwnership` function in [useAssetBrowser.ts](src/platform/assets/composables/useAssetBrowser.ts#L38-L45) to filter by `is_immutable` property - **i18n**: Added translation strings for ownership filter options - **Tests**: Added comprehensive tests for ownership filtering in both composable and component test files ## Review Focus - The ownership filter visibility logic correctly checks for mutable assets (`!is_immutable`) - Default filter value is 'all' to show all models initially - Filter integrates cleanly with existing file format and base model filters 🤖 Generated with [Claude Code](https://claude.com/claude-code) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7201-feat-Add-ownership-filter-to-model-browser-2c16d73d365081f280f6d1e42e5400af) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
committed by
GitHub
parent
2b9f7ecedf
commit
6850c45d63
@@ -2160,15 +2160,19 @@
|
||||
"noModelsInFolder": "No {type} available in this folder",
|
||||
"notSureLeaveAsIs": "Not sure? Just leave this as is",
|
||||
"onlyCivitaiUrlsSupported": "Only Civitai URLs are supported",
|
||||
"ownership": "Ownership",
|
||||
"ownershipAll": "All",
|
||||
"ownershipMyModels": "My models",
|
||||
"ownershipPublicModels": "Public models",
|
||||
"selectFrameworks": "Select Frameworks",
|
||||
"selectModelType": "Select model type",
|
||||
"selectProjects": "Select Projects",
|
||||
"sortAZ": "A-Z",
|
||||
"sortBy": "Sort by",
|
||||
"sortingType": "Sorting Type",
|
||||
"sortPopular": "Popular",
|
||||
"sortRecent": "Recent",
|
||||
"sortZA": "Z-A",
|
||||
"sortingType": "Sorting Type",
|
||||
"tags": "Tags",
|
||||
"tagsHelp": "Separate tags with commas",
|
||||
"tagsPlaceholder": "e.g., models, checkpoint",
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<template #contentFilter>
|
||||
<AssetFilterBar
|
||||
:assets="categoryFilteredAssets"
|
||||
:all-assets="fetchedAssets"
|
||||
@filter-change="updateFilters"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -26,6 +26,16 @@
|
||||
data-component-id="asset-filter-base-models"
|
||||
@update:model-value="handleFilterChange"
|
||||
/>
|
||||
|
||||
<SingleSelect
|
||||
v-if="hasMutableAssets"
|
||||
v-model="ownership"
|
||||
:label="$t('assetBrowser.ownership')"
|
||||
:options="ownershipOptions"
|
||||
class="min-w-42"
|
||||
data-component-id="asset-filter-ownership"
|
||||
@update:model-value="handleFilterChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center" data-component-id="asset-filter-bar-right">
|
||||
@@ -46,21 +56,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import MultiSelect from '@/components/input/MultiSelect.vue'
|
||||
import SingleSelect from '@/components/input/SingleSelect.vue'
|
||||
import type { SelectOption } from '@/components/input/types'
|
||||
import { t } from '@/i18n'
|
||||
import type { OwnershipOption } from '@/platform/assets/composables/useAssetBrowser'
|
||||
import { useAssetFilterOptions } from '@/platform/assets/composables/useAssetFilterOptions'
|
||||
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
||||
|
||||
export interface FilterState {
|
||||
fileFormats: string[]
|
||||
baseModels: string[]
|
||||
sortBy: string
|
||||
}
|
||||
|
||||
const SORT_OPTIONS = [
|
||||
{ name: t('assetBrowser.sortRecent'), value: 'recent' },
|
||||
{ name: t('assetBrowser.sortAZ'), value: 'name-asc' },
|
||||
@@ -71,17 +76,37 @@ type SortOption = (typeof SORT_OPTIONS)[number]['value']
|
||||
|
||||
const sortOptions = [...SORT_OPTIONS]
|
||||
|
||||
const { assets = [] } = defineProps<{
|
||||
const ownershipOptions = [
|
||||
{ name: t('assetBrowser.ownershipAll'), value: 'all' },
|
||||
{ name: t('assetBrowser.ownershipMyModels'), value: 'my-models' },
|
||||
{ name: t('assetBrowser.ownershipPublicModels'), value: 'public-models' }
|
||||
]
|
||||
|
||||
export interface FilterState {
|
||||
fileFormats: string[]
|
||||
baseModels: string[]
|
||||
sortBy: string
|
||||
ownership: OwnershipOption
|
||||
}
|
||||
|
||||
const { assets = [], allAssets = [] } = defineProps<{
|
||||
assets?: AssetItem[]
|
||||
allAssets?: AssetItem[]
|
||||
}>()
|
||||
|
||||
const fileFormats = ref<SelectOption[]>([])
|
||||
const baseModels = ref<SelectOption[]>([])
|
||||
const sortBy = ref<SortOption>('recent')
|
||||
const ownership = ref<OwnershipOption>('all')
|
||||
|
||||
const { availableFileFormats, availableBaseModels } =
|
||||
useAssetFilterOptions(assets)
|
||||
|
||||
const hasMutableAssets = computed(() => {
|
||||
const assetsToCheck = allAssets.length ? allAssets : assets
|
||||
return assetsToCheck.some((asset) => asset.is_immutable === false)
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
filterChange: [filters: FilterState]
|
||||
}>()
|
||||
@@ -90,7 +115,8 @@ function handleFilterChange() {
|
||||
emit('filterChange', {
|
||||
fileFormats: fileFormats.value.map((option: SelectOption) => option.value),
|
||||
baseModels: baseModels.value.map((option: SelectOption) => option.value),
|
||||
sortBy: sortBy.value
|
||||
sortBy: sortBy.value,
|
||||
ownership: ownership.value
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
getAssetDescription
|
||||
} from '@/platform/assets/utils/assetMetadataUtils'
|
||||
|
||||
export type OwnershipOption = 'all' | 'my-models' | 'public-models'
|
||||
|
||||
function filterByCategory(category: string) {
|
||||
return (asset: AssetItem) => {
|
||||
return category === 'all' || asset.tags.includes(category)
|
||||
@@ -35,6 +37,15 @@ function filterByBaseModels(models: string[]) {
|
||||
}
|
||||
}
|
||||
|
||||
function filterByOwnership(ownership: OwnershipOption) {
|
||||
return (asset: AssetItem) => {
|
||||
if (ownership === 'all') return true
|
||||
if (ownership === 'my-models') return asset.is_immutable === false
|
||||
if (ownership === 'public-models') return asset.is_immutable === true
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
type AssetBadge = {
|
||||
label: string
|
||||
type: 'type' | 'base' | 'size'
|
||||
@@ -65,7 +76,8 @@ export function useAssetBrowser(
|
||||
const filters = ref<FilterState>({
|
||||
sortBy: 'recent',
|
||||
fileFormats: [],
|
||||
baseModels: []
|
||||
baseModels: [],
|
||||
ownership: 'all'
|
||||
})
|
||||
|
||||
// Transform API asset to display asset
|
||||
@@ -176,6 +188,7 @@ export function useAssetBrowser(
|
||||
const filtered = searchFiltered.value
|
||||
.filter(filterByFileFormats(filters.value.fileFormats))
|
||||
.filter(filterByBaseModels(filters.value.baseModels))
|
||||
.filter(filterByOwnership(filters.value.ownership))
|
||||
|
||||
const sortedAssets = [...filtered]
|
||||
sortedAssets.sort((a, b) => {
|
||||
|
||||
@@ -146,9 +146,15 @@ export function createAssetWithoutUserMetadata() {
|
||||
return asset
|
||||
}
|
||||
|
||||
export function createAssetWithSpecificExtension(extension: string) {
|
||||
export function createAssetWithSpecificExtension(
|
||||
extension: string,
|
||||
isImmutable?: boolean
|
||||
) {
|
||||
const asset = createMockAssets(1)[0]
|
||||
asset.name = `test-model.${extension}`
|
||||
if (isImmutable !== undefined) {
|
||||
asset.is_immutable = isImmutable
|
||||
}
|
||||
return asset
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user