From 4cf55a7215e28d156b14a4bef89813ae97c2bd6c Mon Sep 17 00:00:00 2001 From: Jin Yi Date: Wed, 28 Jan 2026 14:11:46 +0900 Subject: [PATCH] [refactor] Manager dialog design improvements (#8247) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Refactored Manager dialog components for cleaner button rendering logic and improved visibility. ## Changes - **InfoPanelHeader**: Centralized button rendering logic (try update, uninstall, install) instead of using slot overrides - **PackTryUpdateButton**: Changed variant from `textonly` to `inverted` for better visibility - **InfoPanel**: Removed slot override and unused imports, simplified component - **ManagerDialog**: Design modifications for improved UX - **PackCard/PackBanner**: Component refinements - **useManagerDisplayPacks**: New composable for display pack management ## Review Focus - Button visibility and styling with `inverted` variant - Centralized button logic in InfoPanelHeader ## Before [manager-before.webm](https://github.com/user-attachments/assets/4cef5f10-9cb2-4a31-a095-b170643e481d) ## After [manager-after.webm](https://github.com/user-attachments/assets/9b693b32-59ca-4c88-b7e7-9a78aba19df7) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8247-refactor-Manager-dialog-design-improvements-2f06d73d365081bf9f07efa043caa378) by [Unito](https://www.unito.io) --- src/components/widget/nav/NavItem.vue | 2 +- src/locales/en/main.json | 15 + .../components/manager/ManagerDialog.vue | 391 +++++++----------- .../manager/button/PackInstallButton.vue | 3 +- .../manager/button/PackTryUpdateButton.vue | 3 +- .../manager/button/PackUninstallButton.vue | 8 +- .../manager/button/PackUpdateButton.vue | 7 +- .../manager/infoPanel/InfoPanel.vue | 207 ++++++---- .../manager/infoPanel/InfoPanelHeader.vue | 97 ----- .../manager/infoPanel/InfoPanelMultiItem.vue | 126 +++--- .../components/manager/infoPanel/InfoTabs.vue | 89 ---- .../manager/infoPanel/InfoTextSection.vue | 38 -- .../manager/infoPanel/MetadataRow.vue | 15 - .../tabs/DescriptionTabPanel.test.ts | 50 +-- .../infoPanel/tabs/DescriptionTabPanel.vue | 98 +++-- .../manager/infoPanel/tabs/NodesTabPanel.vue | 16 +- .../infoPanel/tabs/WarningTabPanel.vue | 9 +- .../manager/packBanner/PackBanner.vue | 2 +- .../manager/packCard/PackCard.test.ts | 14 +- .../components/manager/packCard/PackCard.vue | 77 ++-- .../manager/packCard/PackCardFooter.vue | 1 + .../components/manager/packIcon/PackIcon.vue | 52 --- .../manager/packIcon/PackIconStacked.vue | 33 -- .../composables/useManagerDisplayPacks.ts | 217 ++++++++++ .../manager/composables/useRegistrySearch.ts | 7 +- .../manager/types/comfyManagerTypes.ts | 8 +- 26 files changed, 727 insertions(+), 858 deletions(-) delete mode 100644 src/workbench/extensions/manager/components/manager/infoPanel/InfoPanelHeader.vue delete mode 100644 src/workbench/extensions/manager/components/manager/infoPanel/InfoTabs.vue delete mode 100644 src/workbench/extensions/manager/components/manager/infoPanel/InfoTextSection.vue delete mode 100644 src/workbench/extensions/manager/components/manager/infoPanel/MetadataRow.vue delete mode 100644 src/workbench/extensions/manager/components/manager/packIcon/PackIcon.vue delete mode 100644 src/workbench/extensions/manager/components/manager/packIcon/PackIconStacked.vue create mode 100644 src/workbench/extensions/manager/composables/useManagerDisplayPacks.ts diff --git a/src/components/widget/nav/NavItem.vue b/src/components/widget/nav/NavItem.vue index f7b8bbcf1..19579074d 100644 --- a/src/components/widget/nav/NavItem.vue +++ b/src/components/widget/nav/NavItem.vue @@ -3,7 +3,7 @@ v-tooltip.right="{ value: tooltipText, disabled: !isOverflowing, - pt: { text: { class: 'whitespace-nowrap' } } + pt: { text: { class: 'w-max whitespace-nowrap' } } }" class="flex cursor-pointer select-none items-center-safe gap-2 rounded-md px-4 py-3 text-sm transition-colors text-base-foreground" :class=" diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 1cbe43d10..b3465916b 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -282,6 +282,10 @@ }, "manager": { "title": "Nodes Manager", + "nodePackInfo": "Node Pack Info", + "basicInfo": "Basic Info", + "actions": "Actions", + "selected": "Selected", "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", @@ -295,6 +299,17 @@ "changingVersion": "Changing version from {from} to {to}", "dependencies": "Dependencies", "inWorkflow": "In Workflow", + "nav": { + "allExtensions": "All Extensions", + "notInstalled": "Not Installed", + "installedSection": "INSTALLED", + "allInstalled": "All installed", + "updatesAvailable": "Updates Available", + "conflicting": "Conflicting", + "inWorkflowSection": "IN WORKFLOW", + "allInWorkflow": "All in: {workflowName}", + "missingNodes": "Missing Nodes" + }, "infoPanelEmpty": "Click an item to see the info", "applyChanges": "Apply Changes", "restartToApplyChanges": "To apply changes, please restart ComfyUI", diff --git a/src/workbench/extensions/manager/components/manager/ManagerDialog.vue b/src/workbench/extensions/manager/components/manager/ManagerDialog.vue index 887243315..97f41da56 100644 --- a/src/workbench/extensions/manager/components/manager/ManagerDialog.vue +++ b/src/workbench/extensions/manager/components/manager/ManagerDialog.vue @@ -2,6 +2,7 @@ @@ -76,37 +109,18 @@ - -
-
- - -
- - -
- - - -
+ +
+ + +
@@ -115,7 +129,7 @@
-
+
workflowStore.activeWorkflow?.filename ?? t('manager.inWorkflow') +) + // Navigation items for LeftSidePanel -const navItems = computed(() => [ - { id: ManagerTab.All, label: t('g.all'), icon: 'pi pi-list' }, - { id: ManagerTab.Installed, label: t('g.installed'), icon: 'pi pi-box' }, +const navItems = computed<(NavItemData | NavGroupData)[]>(() => [ { - id: ManagerTab.Workflow, - label: t('manager.inWorkflow'), - icon: 'pi pi-folder' + id: ManagerTab.All, + label: t('manager.nav.allExtensions'), + icon: 'icon-[lucide--list]' }, { - id: ManagerTab.Missing, - label: t('g.missing'), - icon: 'pi pi-exclamation-circle' + id: ManagerTab.NotInstalled, + label: t('manager.nav.notInstalled'), + icon: 'icon-[lucide--globe]' }, { - id: ManagerTab.UpdateAvailable, - label: t('g.updateAvailable'), - icon: 'pi pi-sync' + title: t('manager.nav.installedSection'), + items: [ + { + id: ManagerTab.AllInstalled, + label: t('manager.nav.allInstalled'), + icon: 'icon-[lucide--download]' + }, + { + id: ManagerTab.UpdateAvailable, + label: t('manager.nav.updatesAvailable'), + icon: 'icon-[lucide--refresh-cw]' + }, + { + id: ManagerTab.Conflicting, + label: t('manager.nav.conflicting'), + icon: 'icon-[lucide--triangle-alert]', + badge: conflictDetectionStore.conflictedPackages.length || undefined + } + ] + }, + { + title: t('manager.nav.inWorkflowSection'), + items: [ + { + id: ManagerTab.Workflow, + label: t('manager.nav.allInWorkflow', { + workflowName: workflowName.value + }), + icon: 'icon-[lucide--share-2]' + }, + { + id: ManagerTab.Missing, + label: t('manager.nav.missingNodes'), + icon: 'icon-[lucide--triangle-alert]' + } + ] } ]) const initialTabId = initialTab ?? initialState.selectedTabId ?? ManagerTab.All const selectedNavId = ref(initialTabId) +// Helper to find a nav item by id in the nested structure +const findNavItemById = ( + items: (NavItemData | NavGroupData)[], + id: string | null +): NavItemData | undefined => { + for (const item of items) { + if ('items' in item) { + const found = item.items.find((subItem) => subItem.id === id) + if (found) return found + } else if (item.id === id) { + return item + } + } + return undefined +} + const selectedTab = computed(() => - navItems.value.find((item) => item.id === selectedNavId.value) + findNavItemById(navItems.value, selectedNavId.value) ) const { @@ -315,120 +383,20 @@ const isInitialLoad = computed( () => searchResults.value.length === 0 && searchQuery.value === '' ) -const isEmptySearch = computed(() => searchQuery.value === '') -const displayPacks = ref([]) - +// Use the new composable for tab-based display packs const { - startFetchInstalled, - filterInstalledPack, - installedPacks, - isLoading: isLoadingInstalled, - isReady: installedPacksReady -} = useInstalledPacks() - -const { - startFetchWorkflowPacks, - filterWorkflowPack, - workflowPacks, - isLoading: isLoadingWorkflow, - isReady: workflowPacksReady -} = useWorkflowPacks() - -const filterMissingPacks = (packs: components['schemas']['Node'][]) => - packs.filter((pack) => !comfyManagerStore.isPackInstalled(pack.id)) + displayPacks, + isLoading: isTabLoading, + workflowPacks +} = useManagerDisplayPacks(selectedNavId, searchResults, searchQuery, sortField) +// Tab helpers for template const isUpdateAvailableTab = computed( () => selectedTab.value?.id === ManagerTab.UpdateAvailable ) -const isInstalledTab = computed( - () => selectedTab.value?.id === ManagerTab.Installed -) const isMissingTab = computed( () => selectedTab.value?.id === ManagerTab.Missing ) -const isWorkflowTab = computed( - () => selectedTab.value?.id === ManagerTab.Workflow -) -const isAllTab = computed(() => selectedTab.value?.id === ManagerTab.All) - -const isOutdatedPack = (pack: components['schemas']['Node']) => { - const { isUpdateAvailable } = usePackUpdateStatus(pack) - return isUpdateAvailable.value === true -} -const filterOutdatedPacks = (packs: components['schemas']['Node'][]) => - packs.filter(isOutdatedPack) - -watch( - [isUpdateAvailableTab, installedPacks], - async () => { - if (!isUpdateAvailableTab.value) return - - if (!isEmptySearch.value) { - displayPacks.value = filterOutdatedPacks(installedPacks.value) - } else if ( - !installedPacks.value.length && - !installedPacksReady.value && - !isLoadingInstalled.value - ) { - await startFetchInstalled() - } else { - displayPacks.value = filterOutdatedPacks(installedPacks.value) - } - }, - { immediate: true } -) - -watch( - [isInstalledTab, installedPacks], - async () => { - if (!isInstalledTab.value) return - - if (!isEmptySearch.value) { - displayPacks.value = filterInstalledPack(searchResults.value) - } else if ( - !installedPacks.value.length && - !installedPacksReady.value && - !isLoadingInstalled.value - ) { - await startFetchInstalled() - } else { - displayPacks.value = installedPacks.value - } - }, - { immediate: true } -) - -watch( - [isMissingTab, isWorkflowTab, workflowPacks, installedPacks], - async () => { - if (!isWorkflowTab.value && !isMissingTab.value) return - - if (!isEmptySearch.value) { - displayPacks.value = isMissingTab.value - ? filterMissingPacks(filterWorkflowPack(searchResults.value)) - : filterWorkflowPack(searchResults.value) - } else if ( - !workflowPacks.value.length && - !isLoadingWorkflow.value && - !workflowPacksReady.value - ) { - await startFetchWorkflowPacks() - if (isMissingTab.value) { - await startFetchInstalled() - } - } else { - displayPacks.value = isMissingTab.value - ? filterMissingPacks(workflowPacks.value) - : workflowPacks.value - } - }, - { immediate: true } -) - -watch([isAllTab, searchResults], () => { - if (!isAllTab.value) return - displayPacks.value = searchResults.value -}) const onClickWarningLink = () => { window.open( @@ -439,49 +407,9 @@ const onClickWarningLink = () => { ) } -const onResultsChange = () => { - switch (selectedTab.value?.id) { - case ManagerTab.Installed: - displayPacks.value = isEmptySearch.value - ? installedPacks.value - : filterInstalledPack(searchResults.value) - break - case ManagerTab.Workflow: - displayPacks.value = isEmptySearch.value - ? workflowPacks.value - : filterWorkflowPack(searchResults.value) - break - case ManagerTab.Missing: - if (!isEmptySearch.value) { - displayPacks.value = filterMissingPacks( - filterWorkflowPack(searchResults.value) - ) - } - break - case ManagerTab.UpdateAvailable: - displayPacks.value = isEmptySearch.value - ? filterOutdatedPacks(installedPacks.value) - : filterOutdatedPacks(searchResults.value) - break - default: - displayPacks.value = searchResults.value - } -} - -watch(searchResults, onResultsChange, { flush: 'post' }) -watch(() => comfyManagerStore.installedPacksIds, onResultsChange) - const isLoading = computed(() => { if (isSearchLoading.value) return searchResults.value.length === 0 - if (selectedTab.value?.id === ManagerTab.Installed) { - return isLoadingInstalled.value - } - if ( - selectedTab.value?.id === ManagerTab.Workflow || - selectedTab.value?.id === ManagerTab.Missing - ) { - return isLoadingWorkflow.value - } + if (isTabLoading.value) return true return isInitialLoad.value }) @@ -501,14 +429,16 @@ const isRightPanelOpen = ref(false) watch( () => selectedNodePacks.value.length, - (length) => { - isRightPanelOpen.value = length > 0 + (length, oldLength) => { + if (length > 0 && oldLength === 0) { + isRightPanelOpen.value = true + } } ) const getLoadingCount = () => { switch (selectedTab.value?.id) { - case ManagerTab.Installed: + case ManagerTab.AllInstalled: return comfyManagerStore.installedPacksIds?.size case ManagerTab.Workflow: return workflowPacks.value?.length @@ -578,10 +508,6 @@ whenever(selectedNodePack, async () => { if (packIndex !== -1) { selectedNodePacks.value.splice(packIndex, 1, mergedPack) } - const idx = displayPacks.value.findIndex((p) => p.id === mergedPack.id) - if (idx !== -1) { - displayPacks.value.splice(idx, 1, mergedPack) - } } }) @@ -595,6 +521,7 @@ watch([searchQuery, selectedNavId], () => { pageNumber.value = 0 gridContainer.scrollTop = 0 } + unSelectItems() }) watchEffect(() => { diff --git a/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue b/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue index 3000a4a33..0aeab79fe 100644 --- a/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue +++ b/src/workbench/extensions/manager/components/manager/button/PackInstallButton.vue @@ -1,6 +1,6 @@ diff --git a/src/workbench/extensions/manager/components/manager/button/PackTryUpdateButton.vue b/src/workbench/extensions/manager/components/manager/button/PackTryUpdateButton.vue index 218aaf35f..201259560 100644 --- a/src/workbench/extensions/manager/components/manager/button/PackTryUpdateButton.vue +++ b/src/workbench/extensions/manager/components/manager/button/PackTryUpdateButton.vue @@ -1,7 +1,7 @@ diff --git a/src/workbench/extensions/manager/components/manager/button/PackUninstallButton.vue b/src/workbench/extensions/manager/components/manager/button/PackUninstallButton.vue index 7ff3e42aa..cac261f91 100644 --- a/src/workbench/extensions/manager/components/manager/button/PackUninstallButton.vue +++ b/src/workbench/extensions/manager/components/manager/button/PackUninstallButton.vue @@ -1,10 +1,6 @@ diff --git a/src/workbench/extensions/manager/components/manager/infoPanel/InfoPanel.vue b/src/workbench/extensions/manager/components/manager/infoPanel/InfoPanel.vue index 9f25902cd..9537b540b 100644 --- a/src/workbench/extensions/manager/components/manager/infoPanel/InfoPanel.vue +++ b/src/workbench/extensions/manager/components/manager/infoPanel/InfoPanel.vue @@ -1,62 +1,110 @@