diff --git a/src/components/dialog/content/manager/ManagerDialogContent.vue b/src/components/dialog/content/manager/ManagerDialogContent.vue
index 4261ff6ff..0c19ea0e9 100644
--- a/src/components/dialog/content/manager/ManagerDialogContent.vue
+++ b/src/components/dialog/content/manager/ManagerDialogContent.vue
@@ -29,6 +29,7 @@
@@ -166,6 +167,7 @@ const {
isLoading: isSearchLoading,
searchResults,
searchMode,
+ sortField,
suggestions
} = useRegistrySearch()
pageNumber.value = 0
diff --git a/src/components/dialog/content/manager/registrySearchBar/RegistrySearchBar.vue b/src/components/dialog/content/manager/registrySearchBar/RegistrySearchBar.vue
index 413af0438..9fd0cfa3c 100644
--- a/src/components/dialog/content/manager/registrySearchBar/RegistrySearchBar.vue
+++ b/src/components/dialog/content/manager/registrySearchBar/RegistrySearchBar.vue
@@ -56,7 +56,10 @@ import { useI18n } from 'vue-i18n'
import SearchFilterDropdown from '@/components/dialog/content/manager/registrySearchBar/SearchFilterDropdown.vue'
import type { NodesIndexSuggestion } from '@/services/algoliaSearchService'
-import type { PackField, SearchOption } from '@/types/comfyManagerTypes'
+import {
+ type SearchOption,
+ SortableAlgoliaField
+} from '@/types/comfyManagerTypes'
import { components } from '@/types/comfyRegistryTypes'
const { searchResults } = defineProps<{
@@ -66,7 +69,9 @@ const { searchResults } = defineProps<{
const searchQuery = defineModel('searchQuery')
const searchMode = defineModel('searchMode', { default: 'packs' })
-const sortField = defineModel('sortField', { default: 'downloads' })
+const sortField = defineModel('sortField', {
+ default: SortableAlgoliaField.Downloads
+})
const { t } = useI18n()
@@ -74,11 +79,12 @@ const hasResults = computed(
() => searchQuery.value?.trim() && searchResults?.length
)
-const sortOptions: SearchOption[] = [
- { id: 'downloads', label: t('manager.sort.downloads') },
- { id: 'name', label: t('g.name') },
- { id: 'rating', label: t('manager.sort.rating') },
- { id: 'category', label: t('g.category') }
+const sortOptions: SearchOption[] = [
+ { id: SortableAlgoliaField.Downloads, label: t('manager.sort.downloads') },
+ { id: SortableAlgoliaField.Created, label: t('manager.sort.created') },
+ { id: SortableAlgoliaField.Updated, label: t('manager.sort.updated') },
+ { id: SortableAlgoliaField.Publisher, label: t('manager.sort.publisher') },
+ { id: SortableAlgoliaField.Name, label: t('g.name') }
]
const filterOptions: SearchOption[] = [
{ id: 'packs', label: t('manager.filter.nodePack') },
diff --git a/src/composables/useRegistrySearch.ts b/src/composables/useRegistrySearch.ts
index 7b4234c71..2960dc74a 100644
--- a/src/composables/useRegistrySearch.ts
+++ b/src/composables/useRegistrySearch.ts
@@ -1,5 +1,6 @@
import { watchDebounced } from '@vueuse/core'
-import { memoize } from 'lodash'
+import type { Hit } from 'algoliasearch/dist/lite/browser'
+import { memoize, orderBy } from 'lodash'
import { computed, ref, watch } from 'vue'
import {
@@ -8,17 +9,30 @@ import {
useAlgoliaSearchService
} from '@/services/algoliaSearchService'
import type { NodesIndexSuggestion } from '@/services/algoliaSearchService'
-import { PackField } from '@/types/comfyManagerTypes'
+import { SortableAlgoliaField } from '@/types/comfyManagerTypes'
const SEARCH_DEBOUNCE_TIME = 256
const DEFAULT_PAGE_SIZE = 64
+const DEFAULT_SORT_FIELD = SortableAlgoliaField.Downloads // Set in the index configuration
+
+const SORT_DIRECTIONS: Record = {
+ [SortableAlgoliaField.Downloads]: 'desc',
+ [SortableAlgoliaField.Created]: 'desc',
+ [SortableAlgoliaField.Updated]: 'desc',
+ [SortableAlgoliaField.Publisher]: 'asc',
+ [SortableAlgoliaField.Name]: 'asc'
+}
+
+const isDateField = (field: SortableAlgoliaField): boolean =>
+ field === SortableAlgoliaField.Created ||
+ field === SortableAlgoliaField.Updated
/**
* Composable for managing UI state of Comfy Node Registry search.
*/
export function useRegistrySearch() {
const isLoading = ref(false)
- const sortField = ref('downloads')
+ const sortField = ref(SortableAlgoliaField.Downloads)
const searchMode = ref<'nodes' | 'packs'>('packs')
const pageSize = ref(DEFAULT_PAGE_SIZE)
const pageNumber = ref(0)
@@ -48,6 +62,15 @@ export function useRegistrySearch() {
toRegistryPack,
(algoliaNode: AlgoliaNodePack) => algoliaNode.id
)
+ const getSortValue = (pack: Hit) => {
+ if (isDateField(sortField.value)) {
+ const value = pack[sortField.value]
+ return value ? new Date(value).getTime() : 0
+ } else {
+ const value = pack[sortField.value]
+ return value ?? 0
+ }
+ }
const updateSearchResults = async (options: { append?: boolean }) => {
isLoading.value = true
@@ -62,10 +85,22 @@ export function useRegistrySearch() {
restrictSearchableAttributes: searchAttributes.value
}
)
+
+ let sortedPacks = nodePacks
+
+ // Results are sorted by the default field to begin with -- so don't manually sort again
+ if (sortField.value && sortField.value !== DEFAULT_SORT_FIELD) {
+ sortedPacks = orderBy(
+ nodePacks,
+ [getSortValue],
+ [SORT_DIRECTIONS[sortField.value]]
+ )
+ }
+
if (options.append && results.value?.length) {
- results.value = results.value.concat(nodePacks)
+ results.value = results.value.concat(sortedPacks)
} else {
- results.value = nodePacks
+ results.value = sortedPacks
}
suggestions.value = querySuggestions
isLoading.value = false
diff --git a/src/locales/en/main.json b/src/locales/en/main.json
index dcc2c749f..1db35b1b6 100644
--- a/src/locales/en/main.json
+++ b/src/locales/en/main.json
@@ -154,8 +154,10 @@
"unknown": "Unknown"
},
"sort": {
- "rating": "Rating",
- "downloads": "Most Popular"
+ "downloads": "Most Popular",
+ "publisher": "Publisher",
+ "created": "Newest",
+ "updated": "Updated Recently"
},
"filter": {
"nodePack": "Node Pack",
diff --git a/src/locales/es/main.json b/src/locales/es/main.json
index f3876a914..b1a136d6d 100644
--- a/src/locales/es/main.json
+++ b/src/locales/es/main.json
@@ -438,8 +438,10 @@
"searchPlaceholder": "Buscar",
"selectVersion": "Seleccionar Versión",
"sort": {
+ "created": "Más reciente",
"downloads": "Más Popular",
- "rating": "Calificación"
+ "publisher": "Editor",
+ "updated": "Actualizado recientemente"
},
"status": {
"active": "Activo",
diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json
index 6c03cf198..159b9ff85 100644
--- a/src/locales/fr/main.json
+++ b/src/locales/fr/main.json
@@ -438,8 +438,10 @@
"searchPlaceholder": "Recherche",
"selectVersion": "Sélectionner la version",
"sort": {
+ "created": "Le plus récent",
"downloads": "Le plus populaire",
- "rating": "Évaluation"
+ "publisher": "Éditeur",
+ "updated": "Mis à jour récemment"
},
"status": {
"active": "Actif",
diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json
index 9f2f9e627..d489c5ac1 100644
--- a/src/locales/ja/main.json
+++ b/src/locales/ja/main.json
@@ -438,8 +438,10 @@
"searchPlaceholder": "検索",
"selectVersion": "バージョンを選択",
"sort": {
+ "created": "最新",
"downloads": "最も人気",
- "rating": "評価"
+ "publisher": "出版社",
+ "updated": "最近更新"
},
"status": {
"active": "アクティブ",
diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json
index 763a5c18a..40ff7afcf 100644
--- a/src/locales/ko/main.json
+++ b/src/locales/ko/main.json
@@ -438,8 +438,10 @@
"searchPlaceholder": "검색",
"selectVersion": "버전 선택",
"sort": {
+ "created": "최신",
"downloads": "가장 인기 있는",
- "rating": "평점"
+ "publisher": "출판사",
+ "updated": "최근 업데이트"
},
"status": {
"active": "활성",
diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json
index fd7864cdd..a849d3c09 100644
--- a/src/locales/ru/main.json
+++ b/src/locales/ru/main.json
@@ -438,8 +438,10 @@
"searchPlaceholder": "Поиск",
"selectVersion": "Выберите версию",
"sort": {
+ "created": "Новейшие",
"downloads": "Самые популярные",
- "rating": "Рейтинг"
+ "publisher": "Издатель",
+ "updated": "Недавно обновленные"
},
"status": {
"active": "Активный",
diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json
index 527b943f8..1f90e505d 100644
--- a/src/locales/zh/main.json
+++ b/src/locales/zh/main.json
@@ -438,8 +438,10 @@
"searchPlaceholder": "搜索",
"selectVersion": "选择版本",
"sort": {
+ "created": "最新",
"downloads": "最受欢迎",
- "rating": "评级"
+ "publisher": "出版商",
+ "updated": "最近更新"
},
"status": {
"active": "活跃",
diff --git a/src/services/algoliaSearchService.ts b/src/services/algoliaSearchService.ts
index 61f68b4a4..1af48a86d 100644
--- a/src/services/algoliaSearchService.ts
+++ b/src/services/algoliaSearchService.ts
@@ -61,6 +61,7 @@ const RETRIEVE_ATTRIBUTES: SearchAttribute[] = [
'status',
'publisher_id',
'total_install',
+ 'create_time',
'update_time',
'license',
'repository_url',
diff --git a/src/types/comfyManagerTypes.ts b/src/types/comfyManagerTypes.ts
index 66edc0d05..c7c687519 100644
--- a/src/types/comfyManagerTypes.ts
+++ b/src/types/comfyManagerTypes.ts
@@ -18,6 +18,14 @@ export enum ManagerTab {
UpdateAvailable = 'updateAvailable'
}
+export enum SortableAlgoliaField {
+ Downloads = 'total_install',
+ Created = 'create_time',
+ Updated = 'update_time',
+ Publisher = 'publisher_id',
+ Name = 'name'
+}
+
export interface TabItem {
id: string
label: string