From d34d6a8a92ea886c790cd2ef9618af4d62dc3733 Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Sun, 1 Feb 2026 23:43:42 -0800 Subject: [PATCH] feat: show legacy manager search tip in no-results empty state (#8537) ## Summary Show a non-intrusive tip in the no-results empty state when users search legacy manager-related terms in the new manager UI. ## Changes - **What**: Add `useLegacySearchTip` composable that detects when search query matches legacy manager keywords ("manager", "comfyui-manager", etc.) and appends a tip to the empty state message suggesting the `--enable-manager-legacy-ui` flag - **Integration**: Tip appears as secondary muted text below "Try a different search" message when no results found - **Tests**: 9 test cases covering detection logic and edge cases ## Review Focus - Keyword list covers common legacy manager search terms - Non-intrusive approach: tip only shows in no-results state, no dismiss button needed Fixes COM-12509 --- src/locales/en/main.json | 1 + .../components/manager/ManagerDialog.vue | 17 ++- .../composables/useLegacySearchTip.test.ts | 126 ++++++++++++++++++ .../manager/composables/useLegacySearchTip.ts | 25 ++++ 4 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 src/workbench/extensions/manager/composables/useLegacySearchTip.test.ts create mode 100644 src/workbench/extensions/manager/composables/useLegacySearchTip.ts diff --git a/src/locales/en/main.json b/src/locales/en/main.json index cd3671bd5..ab7d7933b 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -290,6 +290,7 @@ "legacyMenuNotAvailable": "Legacy manager menu is not available, defaulting to the new manager menu.", "legacyManagerUI": "Use Legacy UI", "legacyManagerUIDescription": "To use the legacy Manager UI, start ComfyUI with --enable-manager-legacy-ui", + "legacyManagerSearchTip": "Looking for ComfyUI-Manager? You can enable the legacy manager UI by starting ComfyUI with the --enable-manager-legacy-ui flag.", "failed": "Failed", "failedToInstall": "Failed to Install", "installError": "Install Error", diff --git a/src/workbench/extensions/manager/components/manager/ManagerDialog.vue b/src/workbench/extensions/manager/components/manager/ManagerDialog.vue index 3800af058..71f10b0a7 100644 --- a/src/workbench/extensions/manager/components/manager/ManagerDialog.vue +++ b/src/workbench/extensions/manager/components/manager/ManagerDialog.vue @@ -206,6 +206,8 @@ import { useManagerDisplayPacks } from '@/workbench/extensions/manager/composabl import { useManagerStatePersistence } from '@/workbench/extensions/manager/composables/useManagerStatePersistence' import { useRegistrySearch } from '@/workbench/extensions/manager/composables/useRegistrySearch' import { useConflictDetectionStore } from '@/workbench/extensions/manager/stores/conflictDetectionStore' +import { useLegacySearchTip } from '@/workbench/extensions/manager/composables/useLegacySearchTip' +import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState' import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore' import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes' @@ -222,6 +224,7 @@ const comfyManagerStore = useComfyManagerStore() const { getPackById } = useComfyRegistryStore() const conflictAcknowledgment = useConflictAcknowledgment() const conflictDetectionStore = useConflictDetectionStore() +const { isNewManagerUI } = useManagerState() const workflowStore = useWorkflowStore() const persistedState = useManagerStatePersistence() const initialState = persistedState.loadStoredState() @@ -349,7 +352,11 @@ const { }) pageNumber.value = 0 -// Filter and sort options for SingleSelect +const { isLegacyManagerSearch } = useLegacySearchTip( + searchQuery, + isNewManagerUI +) + const filterOptions = computed(() => [ { name: t('manager.filter.nodePack'), value: 'packs' }, { name: t('g.nodes'), value: 'nodes' } @@ -414,7 +421,13 @@ const emptyStateTitle = computed(() => { const emptyStateMessage = computed(() => { if (comfyManagerStore.error) return t('manager.tryAgainLater') - if (searchQuery.value) return t('manager.tryDifferentSearch') + if (searchQuery.value) { + const baseMessage = t('manager.tryDifferentSearch') + if (isLegacyManagerSearch.value) { + return `${baseMessage}\n\n${t('manager.legacyManagerSearchTip')}` + } + return baseMessage + } const tabId = selectedTab.value?.id as ManagerTab | undefined const emptyStateKey = tabId ? tabEmptyStateKeys[tabId] : undefined diff --git a/src/workbench/extensions/manager/composables/useLegacySearchTip.test.ts b/src/workbench/extensions/manager/composables/useLegacySearchTip.test.ts new file mode 100644 index 000000000..bb008c625 --- /dev/null +++ b/src/workbench/extensions/manager/composables/useLegacySearchTip.test.ts @@ -0,0 +1,126 @@ +import type { Ref } from 'vue' +import { beforeEach, describe, expect, it } from 'vitest' +import { nextTick, ref } from 'vue' + +import { useLegacySearchTip } from './useLegacySearchTip' + +describe('useLegacySearchTip', () => { + let searchQuery: Ref + let isNewManagerUI: Ref + + beforeEach(() => { + searchQuery = ref('') + isNewManagerUI = ref(true) + }) + + describe('isLegacyManagerSearch', () => { + it('returns true when searching "manager" in new manager UI', async () => { + const { isLegacyManagerSearch } = useLegacySearchTip( + searchQuery, + isNewManagerUI + ) + + searchQuery.value = 'manager' + await nextTick() + + expect(isLegacyManagerSearch.value).toBe(true) + }) + + it('returns true when searching "comfyui-manager"', async () => { + const { isLegacyManagerSearch } = useLegacySearchTip( + searchQuery, + isNewManagerUI + ) + + searchQuery.value = 'comfyui-manager' + await nextTick() + + expect(isLegacyManagerSearch.value).toBe(true) + }) + + it('returns true when searching "comfyui manager" (space variant)', async () => { + const { isLegacyManagerSearch } = useLegacySearchTip( + searchQuery, + isNewManagerUI + ) + + searchQuery.value = 'comfyui manager' + await nextTick() + + expect(isLegacyManagerSearch.value).toBe(true) + }) + + it('returns true when keyword is part of larger query', async () => { + const { isLegacyManagerSearch } = useLegacySearchTip( + searchQuery, + isNewManagerUI + ) + + searchQuery.value = 'where is manager' + await nextTick() + + expect(isLegacyManagerSearch.value).toBe(true) + }) + + it('matches case-insensitively', async () => { + const { isLegacyManagerSearch } = useLegacySearchTip( + searchQuery, + isNewManagerUI + ) + + searchQuery.value = 'MANAGER' + await nextTick() + + expect(isLegacyManagerSearch.value).toBe(true) + }) + + it('returns false when query is empty', async () => { + const { isLegacyManagerSearch } = useLegacySearchTip( + searchQuery, + isNewManagerUI + ) + + searchQuery.value = '' + await nextTick() + + expect(isLegacyManagerSearch.value).toBe(false) + }) + + it('returns false when query is whitespace only', async () => { + const { isLegacyManagerSearch } = useLegacySearchTip( + searchQuery, + isNewManagerUI + ) + + searchQuery.value = ' ' + await nextTick() + + expect(isLegacyManagerSearch.value).toBe(false) + }) + + it('returns false when searching unrelated terms', async () => { + const { isLegacyManagerSearch } = useLegacySearchTip( + searchQuery, + isNewManagerUI + ) + + searchQuery.value = 'controlnet' + await nextTick() + + expect(isLegacyManagerSearch.value).toBe(false) + }) + + it('returns false when isNewManagerUI is false', async () => { + isNewManagerUI.value = false + const { isLegacyManagerSearch } = useLegacySearchTip( + searchQuery, + isNewManagerUI + ) + + searchQuery.value = 'manager' + await nextTick() + + expect(isLegacyManagerSearch.value).toBe(false) + }) + }) +}) diff --git a/src/workbench/extensions/manager/composables/useLegacySearchTip.ts b/src/workbench/extensions/manager/composables/useLegacySearchTip.ts new file mode 100644 index 000000000..0cab1e961 --- /dev/null +++ b/src/workbench/extensions/manager/composables/useLegacySearchTip.ts @@ -0,0 +1,25 @@ +import type { Ref } from 'vue' +import { computed } from 'vue' + +const LEGACY_MANAGER_KEYWORDS = [ + 'manager', + 'comfyui-manager', + 'manager comfyui', + 'comfyui manager' +] as const + +export function useLegacySearchTip( + searchQuery: Ref, + isNewManagerUI: Ref +) { + const isLegacyManagerSearch = computed(() => { + if (!isNewManagerUI.value) return false + const query = searchQuery.value.toLowerCase().trim() + if (!query) return false + return LEGACY_MANAGER_KEYWORDS.some((keyword) => query.includes(keyword)) + }) + + return { + isLegacyManagerSearch + } +}