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 @@