diff --git a/src/composables/useTemplateFiltering.ts b/src/composables/useTemplateFiltering.ts index 4d399e757..c181e2f91 100644 --- a/src/composables/useTemplateFiltering.ts +++ b/src/composables/useTemplateFiltering.ts @@ -1,8 +1,9 @@ -import { refDebounced } from '@vueuse/core' +import { refDebounced, watchDebounced } from '@vueuse/core' import Fuse from 'fuse.js' import { computed, ref, watch } from 'vue' import type { Ref } from 'vue' +import { useSettingStore } from '@/platform/settings/settingStore' import { useTelemetry } from '@/platform/telemetry' import type { TemplateInfo } from '@/platform/workflow/templates/types/template' import { debounce } from 'es-toolkit/compat' @@ -10,17 +11,25 @@ import { debounce } from 'es-toolkit/compat' export function useTemplateFiltering( templates: Ref | TemplateInfo[] ) { + const settingStore = useSettingStore() + const searchQuery = ref('') - const selectedModels = ref([]) - const selectedUseCases = ref([]) - const selectedRunsOn = ref([]) + const selectedModels = ref( + settingStore.get('Comfy.Templates.SelectedModels') + ) + const selectedUseCases = ref( + settingStore.get('Comfy.Templates.SelectedUseCases') + ) + const selectedRunsOn = ref( + settingStore.get('Comfy.Templates.SelectedRunsOn') + ) const sortBy = ref< | 'default' | 'alphabetical' | 'newest' | 'vram-low-to-high' | 'model-size-low-to-high' - >('newest') + >(settingStore.get('Comfy.Templates.SortBy')) const templatesArray = computed(() => { const templateData = 'value' in templates ? templates.value : templates @@ -197,7 +206,7 @@ export function useTemplateFiltering( selectedModels.value = [] selectedUseCases.value = [] selectedRunsOn.value = [] - sortBy.value = 'default' + sortBy.value = 'newest' } const removeModelFilter = (model: string) => { @@ -247,6 +256,39 @@ export function useTemplateFiltering( { deep: true } ) + // Persist filter changes to settings (debounced to avoid excessive saves) + watchDebounced( + selectedModels, + (newValue) => { + void settingStore.set('Comfy.Templates.SelectedModels', newValue) + }, + { debounce: 500, deep: true } + ) + + watchDebounced( + selectedUseCases, + (newValue) => { + void settingStore.set('Comfy.Templates.SelectedUseCases', newValue) + }, + { debounce: 500, deep: true } + ) + + watchDebounced( + selectedRunsOn, + (newValue) => { + void settingStore.set('Comfy.Templates.SelectedRunsOn', newValue) + }, + { debounce: 500, deep: true } + ) + + watchDebounced( + sortBy, + (newValue) => { + void settingStore.set('Comfy.Templates.SortBy', newValue) + }, + { debounce: 500 } + ) + return { // State searchQuery, diff --git a/src/platform/settings/constants/coreSettings.ts b/src/platform/settings/constants/coreSettings.ts index 2ae706bb7..5d47e1e80 100644 --- a/src/platform/settings/constants/coreSettings.ts +++ b/src/platform/settings/constants/coreSettings.ts @@ -1053,6 +1053,34 @@ export const CORE_SETTINGS: SettingParams[] = [ defaultValue: 0 }, + /** + * Template Library Filter Settings + */ + { + id: 'Comfy.Templates.SelectedModels', + name: 'Template library - Selected model filters', + type: 'hidden', + defaultValue: [] + }, + { + id: 'Comfy.Templates.SelectedUseCases', + name: 'Template library - Selected use case filters', + type: 'hidden', + defaultValue: [] + }, + { + id: 'Comfy.Templates.SelectedRunsOn', + name: 'Template library - Selected runs on filters', + type: 'hidden', + defaultValue: [] + }, + { + id: 'Comfy.Templates.SortBy', + name: 'Template library - Sort preference', + type: 'hidden', + defaultValue: 'newest' + }, + /** * Vue Node System Settings */ diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts index ce81c0368..ba5a0e16d 100644 --- a/src/schemas/apiSchema.ts +++ b/src/schemas/apiSchema.ts @@ -501,6 +501,17 @@ const zSettings = z.object({ "what's new seen" ]), 'Comfy.Release.Timestamp': z.number(), + /** Template library filter settings */ + 'Comfy.Templates.SelectedModels': z.array(z.string()), + 'Comfy.Templates.SelectedUseCases': z.array(z.string()), + 'Comfy.Templates.SelectedRunsOn': z.array(z.string()), + 'Comfy.Templates.SortBy': z.enum([ + 'default', + 'alphabetical', + 'newest', + 'vram-low-to-high', + 'model-size-low-to-high' + ]), /** Settings used for testing */ 'test.setting': z.any(), 'main.sub.setting.name': z.any(), diff --git a/tests-ui/tests/composables/useTemplateFiltering.test.ts b/tests-ui/tests/composables/useTemplateFiltering.test.ts index b6428704b..00cc7d856 100644 --- a/tests-ui/tests/composables/useTemplateFiltering.test.ts +++ b/tests-ui/tests/composables/useTemplateFiltering.test.ts @@ -1,10 +1,43 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { nextTick, ref } from 'vue' -import { useTemplateFiltering } from '@/composables/useTemplateFiltering' import type { TemplateInfo } from '@/platform/workflow/templates/types/template' +const defaultSettingStore = { + get: vi.fn((key: string) => { + switch (key) { + case 'Comfy.Templates.SelectedModels': + case 'Comfy.Templates.SelectedUseCases': + case 'Comfy.Templates.SelectedRunsOn': + return [] + case 'Comfy.Templates.SortBy': + return 'newest' + default: + return undefined + } + }), + set: vi.fn().mockResolvedValue(undefined) +} + +vi.mock('@/platform/settings/settingStore', () => ({ + useSettingStore: vi.fn(() => defaultSettingStore) +})) + +vi.mock('@/platform/telemetry', () => ({ + useTelemetry: vi.fn(() => ({ + trackTemplateFilterChanged: vi.fn() + })) +})) + +const { useTemplateFiltering } = await import( + '@/composables/useTemplateFiltering' +) + describe('useTemplateFiltering', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + afterEach(() => { vi.useRealTimers() })