mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-21 07:14:11 +00:00
[Manager] Add filtering nodes/packs by 'Installed' or 'All' (#3031)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -15,8 +15,7 @@
|
||||
<ManagerNavSidebar
|
||||
v-if="isSideNavOpen"
|
||||
:tabs="tabs"
|
||||
:selected-tab="selectedTab"
|
||||
@update:selected-tab="handleTabSelection"
|
||||
v-model:selectedTab="selectedTab"
|
||||
/>
|
||||
<div
|
||||
class="flex-1 overflow-auto"
|
||||
@@ -107,7 +106,8 @@
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, watchEffect } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import ContentDivider from '@/components/common/ContentDivider.vue'
|
||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||
@@ -118,12 +118,17 @@ import InfoPanelMultiItem from '@/components/dialog/content/manager/infoPanel/In
|
||||
import PackCard from '@/components/dialog/content/manager/packCard/PackCard.vue'
|
||||
import RegistrySearchBar from '@/components/dialog/content/manager/registrySearchBar/RegistrySearchBar.vue'
|
||||
import { useResponsiveCollapse } from '@/composables/element/useResponsiveCollapse'
|
||||
import { useInstalledPacks } from '@/composables/useInstalledPacks'
|
||||
import { useRegistrySearch } from '@/composables/useRegistrySearch'
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import type { PackField, TabItem } from '@/types/comfyManagerTypes'
|
||||
import { components } from '@/types/comfyRegistryTypes'
|
||||
|
||||
const DEFAULT_CARD_SIZE = 512
|
||||
|
||||
const { t } = useI18n()
|
||||
const comfyManagerStore = useComfyManagerStore()
|
||||
|
||||
const {
|
||||
isSmallScreen,
|
||||
isOpen: isSideNavOpen,
|
||||
@@ -132,14 +137,10 @@ const {
|
||||
const hideSearchBar = computed(() => isSmallScreen.value && showInfoPanel.value)
|
||||
|
||||
const tabs = ref<TabItem[]>([
|
||||
{ id: 'all', label: 'All', icon: 'pi-list' },
|
||||
{ id: 'community', label: 'Community', icon: 'pi-globe' },
|
||||
{ id: 'installed', label: 'Installed', icon: 'pi-box' }
|
||||
{ id: 'all', label: t('g.all'), icon: 'pi-list' },
|
||||
{ id: 'installed', label: t('g.installed'), icon: 'pi-box' }
|
||||
])
|
||||
const selectedTab = ref<TabItem>(tabs.value[0])
|
||||
const handleTabSelection = (tab: TabItem) => {
|
||||
selectedTab.value = tab
|
||||
}
|
||||
|
||||
const { searchQuery, pageNumber, sortField, isLoading, error, searchResults } =
|
||||
useRegistrySearch()
|
||||
@@ -149,8 +150,27 @@ const isInitialLoad = computed(
|
||||
() => searchResults.value.length === 0 && searchQuery.value === ''
|
||||
)
|
||||
|
||||
const { getInstalledPacks } = useInstalledPacks()
|
||||
const displayPacks = ref<components['schemas']['Node'][]>([])
|
||||
const isEmptySearch = computed(() => searchQuery.value === '')
|
||||
|
||||
const getInstalledSearchResults = async () => {
|
||||
if (isEmptySearch.value) return getInstalledPacks()
|
||||
return searchResults.value.filter((pack) =>
|
||||
comfyManagerStore.installedPacksIds.has(pack.name)
|
||||
)
|
||||
}
|
||||
|
||||
watchEffect(async () => {
|
||||
if (selectedTab.value.id === 'installed') {
|
||||
displayPacks.value = await getInstalledSearchResults()
|
||||
} else {
|
||||
displayPacks.value = searchResults.value
|
||||
}
|
||||
})
|
||||
|
||||
const resultsWithKeys = computed(() =>
|
||||
searchResults.value.map((item) => ({
|
||||
displayPacks.value.map((item) => ({
|
||||
...item,
|
||||
key: item.id || item.name
|
||||
}))
|
||||
|
||||
@@ -4,17 +4,16 @@
|
||||
>
|
||||
<ScrollPanel class="w-80 mt-7">
|
||||
<Listbox
|
||||
:model-value="selectedTab"
|
||||
v-model="selectedTab"
|
||||
:options="tabs"
|
||||
optionLabel="label"
|
||||
listStyle="max-height:unset"
|
||||
class="w-full border-0 bg-transparent"
|
||||
:pt="{
|
||||
root: { class: 'w-full border-0 bg-transparent' },
|
||||
list: { class: 'p-5' },
|
||||
option: { class: 'px-8 py-3 text-lg rounded-xl' },
|
||||
optionGroup: { class: 'p-0 text-left text-inherit' }
|
||||
}"
|
||||
@update:model-value="handleTabSelection"
|
||||
>
|
||||
<template #option="slotProps">
|
||||
<div class="text-left flex items-center">
|
||||
@@ -37,14 +36,7 @@ import type { TabItem } from '@/types/comfyManagerTypes'
|
||||
|
||||
defineProps<{
|
||||
tabs: TabItem[]
|
||||
selectedTab: TabItem
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:selectedTab': [value: TabItem]
|
||||
}>()
|
||||
|
||||
const handleTabSelection = (tab: TabItem) => {
|
||||
emit('update:selectedTab', tab)
|
||||
}
|
||||
const selectedTab = defineModel<TabItem>('selectedTab')
|
||||
</script>
|
||||
|
||||
39
src/composables/useInstalledPacks.ts
Normal file
39
src/composables/useInstalledPacks.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { chunk } from 'lodash'
|
||||
import { onUnmounted } from 'vue'
|
||||
|
||||
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
|
||||
import { useComfyRegistryStore } from '@/stores/comfyRegistryStore'
|
||||
import { components } from '@/types/comfyRegistryTypes'
|
||||
|
||||
const MAX_SIMULTANEOUS_REQUESTS = 8
|
||||
|
||||
export const useInstalledPacks = () => {
|
||||
const comfyManagerStore = useComfyManagerStore()
|
||||
const { getPackById, cancelRequests } = useComfyRegistryStore()
|
||||
|
||||
const getInstalledIdsChunks = () =>
|
||||
chunk(
|
||||
Array.from(comfyManagerStore.installedPacksIds),
|
||||
MAX_SIMULTANEOUS_REQUESTS
|
||||
)
|
||||
|
||||
const getInstalledPacks = async () => {
|
||||
const packs: components['schemas']['Node'][] = []
|
||||
for (const packIdsChunk of getInstalledIdsChunks()) {
|
||||
const requests = packIdsChunk.map((id) => getPackById(id))
|
||||
const responses = await Promise.all(requests)
|
||||
responses.forEach((pack) => {
|
||||
if (pack) packs.push(pack)
|
||||
})
|
||||
}
|
||||
return packs
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
cancelRequests()
|
||||
})
|
||||
|
||||
return {
|
||||
getInstalledPacks
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,6 @@ export function useRegistrySearch() {
|
||||
onUnmounted(() => {
|
||||
debouncedSearch.cancel() // Cancel debounced searches
|
||||
registryStore.cancelRequests() // Cancel in-flight requests
|
||||
registryStore.clearCache() // Clear cached responses
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -92,7 +92,9 @@
|
||||
"category": "Category",
|
||||
"sort": "Sort",
|
||||
"filter": "Filter",
|
||||
"apply": "Apply"
|
||||
"apply": "Apply",
|
||||
"enabled": "Enabled",
|
||||
"installed": "Installed"
|
||||
},
|
||||
"manager": {
|
||||
"title": "Custom Nodes Manager",
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"download": "Télécharger",
|
||||
"empty": "Vide",
|
||||
"enableAll": "Activer tout",
|
||||
"enabled": "Activé",
|
||||
"error": "Erreur",
|
||||
"experimental": "BETA",
|
||||
"export": "Exportation",
|
||||
@@ -158,6 +159,7 @@
|
||||
"import": "Importer",
|
||||
"insert": "Insérer",
|
||||
"install": "Installer",
|
||||
"installed": "Installé",
|
||||
"installing": "Installation",
|
||||
"keybinding": "Raccourci clavier",
|
||||
"loadAllFolders": "Charger tous les dossiers",
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"download": "ダウンロード",
|
||||
"empty": "空",
|
||||
"enableAll": "すべて有効にする",
|
||||
"enabled": "有効",
|
||||
"error": "エラー",
|
||||
"experimental": "ベータ",
|
||||
"export": "エクスポート",
|
||||
@@ -158,6 +159,7 @@
|
||||
"import": "インポート",
|
||||
"insert": "挿入",
|
||||
"install": "インストール",
|
||||
"installed": "インストール済み",
|
||||
"installing": "インストール中",
|
||||
"keybinding": "キーバインディング",
|
||||
"loadAllFolders": "すべてのフォルダーを読み込む",
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"download": "다운로드",
|
||||
"empty": "비어 있음",
|
||||
"enableAll": "모두 활성화",
|
||||
"enabled": "활성화됨",
|
||||
"error": "오류",
|
||||
"experimental": "베타",
|
||||
"export": "내보내기",
|
||||
@@ -158,6 +159,7 @@
|
||||
"import": "가져오기",
|
||||
"insert": "삽입",
|
||||
"install": "설치",
|
||||
"installed": "설치됨",
|
||||
"installing": "설치 중",
|
||||
"keybinding": "키 바인딩",
|
||||
"loadAllFolders": "모든 폴더 로드",
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"download": "Скачать",
|
||||
"empty": "Пусто",
|
||||
"enableAll": "Включить все",
|
||||
"enabled": "Включено",
|
||||
"error": "Ошибка",
|
||||
"experimental": "БЕТА",
|
||||
"export": "Экспорт",
|
||||
@@ -158,6 +159,7 @@
|
||||
"import": "Импорт",
|
||||
"insert": "Вставить",
|
||||
"install": "Установить",
|
||||
"installed": "Установлено",
|
||||
"installing": "Установка",
|
||||
"keybinding": "Привязка клавиш",
|
||||
"loadAllFolders": "Загрузить все папки",
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"download": "下载",
|
||||
"empty": "空",
|
||||
"enableAll": "启用全部",
|
||||
"enabled": "已启用",
|
||||
"error": "错误",
|
||||
"experimental": "测试版",
|
||||
"export": "导出",
|
||||
@@ -158,6 +159,7 @@
|
||||
"import": "导入",
|
||||
"insert": "插入",
|
||||
"install": "安装",
|
||||
"installed": "已安装",
|
||||
"installing": "正在安装",
|
||||
"keybinding": "按键绑定",
|
||||
"loadAllFolders": "加载所有文件夹",
|
||||
|
||||
@@ -138,6 +138,7 @@ export const useComfyManagerStore = defineStore('comfyManager', () => {
|
||||
|
||||
// Installed packs state
|
||||
installedPacks,
|
||||
installedPacksIds,
|
||||
isPackInstalled: isInstalledPackId,
|
||||
isPackEnabled: isEnabledPackId,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user