Skip to content

Commit fa14ec5

Browse files
viva-jinyigithub-actionswebfilteredchristian-byrnejtydhr88
authored
[Manager] Impletent “Install All” button (#4196)
Co-authored-by: github-actions <[email protected]> Co-authored-by: filtered <[email protected]> Co-authored-by: Christian Byrne <[email protected]> Co-authored-by: Terry Jia <[email protected]> Co-authored-by: comfy-waifu <[email protected]> Co-authored-by: Comfy Org PR Bot <[email protected]>
1 parent ec9da0b commit fa14ec5

File tree

14 files changed

+346
-32
lines changed

14 files changed

+346
-32
lines changed

src/components/dialog/content/LoadWorkflowWarning.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@
3131
</template>
3232
</ListBox>
3333
<div v-if="isManagerInstalled" class="flex justify-end py-3">
34+
<PackInstallButton
35+
:disabled="isLoading || !!error || missingNodePacks.length === 0"
36+
:node-packs="missingNodePacks"
37+
variant="black"
38+
:label="$t('manager.installAllMissingNodes')"
39+
/>
3440
<Button label="Open Manager" size="small" outlined @click="openManager" />
3541
</div>
3642
</template>
@@ -41,6 +47,8 @@ import ListBox from 'primevue/listbox'
4147
import { computed } from 'vue'
4248
4349
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
50+
import PackInstallButton from '@/components/dialog/content/manager/button/PackInstallButton.vue'
51+
import { useMissingNodes } from '@/composables/nodePack/useMissingNodes'
4452
import { useDialogService } from '@/services/dialogService'
4553
import { useAboutPanelStore } from '@/stores/aboutPanelStore'
4654
import type { MissingNodeType } from '@/types/comfy'
@@ -52,6 +60,9 @@ const props = defineProps<{
5260
5361
const aboutPanelStore = useAboutPanelStore()
5462
63+
// Get missing node packs from workflow with loading and error states
64+
const { missingNodePacks, isLoading, error } = useMissingNodes()
65+
5566
// Determines if ComfyUI-Manager is installed by checking for its badge in the about panel
5667
// This allows us to conditionally show the Manager button only when the extension is available
5768
// TODO: Remove this check when Manager functionality is fully migrated into core

src/components/dialog/content/manager/ManagerDialogContent.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
v-model:sortField="sortField"
3333
:search-results="searchResults"
3434
:suggestions="suggestions"
35+
:is-missing-tab="isMissingTab"
3536
:sort-options="sortOptions"
3637
/>
3738
<div class="flex-1 overflow-auto">

src/components/dialog/content/manager/button/PackActionButton.vue

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
<template>
22
<Button
33
outlined
4-
class="m-0 p-0 rounded-lg border-neutral-700"
5-
:class="{
6-
'w-full': fullWidth,
7-
'w-min-content': !fullWidth
8-
}"
4+
class="!m-0 p-0 rounded-lg"
5+
:class="[
6+
variant === 'black'
7+
? 'bg-neutral-900 text-white border-neutral-900'
8+
: 'border-neutral-700',
9+
fullWidth ? 'w-full' : 'w-min-content'
10+
]"
911
:disabled="loading"
1012
v-bind="$attrs"
1113
@click="onClick"
1214
>
13-
<span class="py-2.5 px-3">
15+
<span class="py-2.5 px-3 whitespace-nowrap">
1416
<template v-if="loading">
1517
{{ loadingMessage ?? $t('g.loading') }}
1618
</template>
@@ -27,12 +29,14 @@ import Button from 'primevue/button'
2729
const {
2830
label,
2931
loadingMessage,
30-
fullWidth = false
32+
fullWidth = false,
33+
variant = 'default'
3134
} = defineProps<{
3235
label: string
3336
loading?: boolean
3437
loadingMessage?: string
3538
fullWidth?: boolean
39+
variant?: 'default' | 'black'
3640
}>()
3741
3842
const emit = defineEmits<{

src/components/dialog/content/manager/button/PackInstallButton.vue

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
<PackActionButton
33
v-bind="$attrs"
44
:label="
5-
nodePacks.length > 1 ? $t('manager.installSelected') : $t('g.install')
5+
label ??
6+
(nodePacks.length > 1 ? $t('manager.installSelected') : $t('g.install'))
67
"
7-
severity="secondary"
8+
:severity="variant === 'black' ? undefined : 'secondary'"
9+
:variant="variant"
810
:loading="isInstalling"
911
:loading-message="$t('g.installing')"
1012
@action="installAllPacks"
@@ -27,8 +29,10 @@ import type { components } from '@/types/comfyRegistryTypes'
2729
2830
type NodePack = components['schemas']['Node']
2931
30-
const { nodePacks } = defineProps<{
32+
const { nodePacks, variant, label } = defineProps<{
3133
nodePacks: NodePack[]
34+
variant?: 'default' | 'black'
35+
label?: string
3236
}>()
3337
3438
const isInstalling = inject(IsInstallingKey, ref(false))

src/components/dialog/content/manager/registrySearchBar/RegistrySearchBar.vue

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,37 @@
11
<template>
22
<div class="relative w-full p-6">
3-
<div class="flex items-center w-full">
4-
<AutoComplete
5-
v-model.lazy="searchQuery"
6-
:suggestions="suggestions || []"
7-
:placeholder="$t('manager.searchPlaceholder')"
8-
:complete-on-focus="false"
9-
:delay="8"
10-
option-label="query"
11-
class="w-full"
12-
:pt="{
13-
pcInputText: {
14-
root: {
15-
autofocus: true,
16-
class: 'w-5/12 rounded-2xl'
3+
<div class="h-12 flex items-center gap-1 justify-between">
4+
<div class="flex items-center w-5/12">
5+
<AutoComplete
6+
v-model.lazy="searchQuery"
7+
:suggestions="suggestions || []"
8+
:placeholder="$t('manager.searchPlaceholder')"
9+
:complete-on-focus="false"
10+
:delay="8"
11+
option-label="query"
12+
class="w-full"
13+
:pt="{
14+
pcInputText: {
15+
root: {
16+
autofocus: true,
17+
class: 'w-full rounded-2xl'
18+
}
19+
},
20+
loader: {
21+
style: 'display: none'
1722
}
18-
},
19-
loader: {
20-
style: 'display: none'
21-
}
22-
}"
23-
:show-empty-message="false"
24-
@complete="stubTrue"
25-
@option-select="onOptionSelect"
23+
}"
24+
:show-empty-message="false"
25+
@complete="stubTrue"
26+
@option-select="onOptionSelect"
27+
/>
28+
</div>
29+
<PackInstallButton
30+
v-if="isMissingTab && missingNodePacks.length > 0"
31+
variant="black"
32+
:disabled="isLoading || !!error"
33+
:node-packs="missingNodePacks"
34+
:label="$t('manager.installAllMissingNodes')"
2635
/>
2736
</div>
2837
<div class="flex mt-3 text-sm">
@@ -55,7 +64,9 @@ import AutoComplete, {
5564
import { computed } from 'vue'
5665
import { useI18n } from 'vue-i18n'
5766
67+
import PackInstallButton from '@/components/dialog/content/manager/button/PackInstallButton.vue'
5868
import SearchFilterDropdown from '@/components/dialog/content/manager/registrySearchBar/SearchFilterDropdown.vue'
69+
import { useMissingNodes } from '@/composables/nodePack/useMissingNodes'
5970
import {
6071
type SearchOption,
6172
SortableAlgoliaField
@@ -71,6 +82,7 @@ const { searchResults, sortOptions } = defineProps<{
7182
searchResults?: components['schemas']['Node'][]
7283
suggestions?: QuerySuggestion[]
7384
sortOptions?: SortableField[]
85+
isMissingTab?: boolean
7486
}>()
7587
7688
const searchQuery = defineModel<string>('searchQuery')
@@ -81,6 +93,9 @@ const sortField = defineModel<string>('sortField', {
8193
8294
const { t } = useI18n()
8395
96+
// Get missing node packs from workflow with loading and error states
97+
const { missingNodePacks, isLoading, error } = useMissingNodes()
98+
8499
const hasResults = computed(
85100
() => searchQuery.value?.trim() && searchResults?.length
86101
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { computed, onMounted } from 'vue'
2+
3+
import { useWorkflowPacks } from '@/composables/nodePack/useWorkflowPacks'
4+
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
5+
import type { components } from '@/types/comfyRegistryTypes'
6+
7+
/**
8+
* Composable to find missing NodePacks from workflow
9+
* Uses the same filtering approach as ManagerDialogContent.vue
10+
* Automatically fetches workflow pack data when initialized
11+
*/
12+
export const useMissingNodes = () => {
13+
const comfyManagerStore = useComfyManagerStore()
14+
const { workflowPacks, isLoading, error, startFetchWorkflowPacks } =
15+
useWorkflowPacks()
16+
17+
// Same filtering logic as ManagerDialogContent.vue
18+
const filterMissingPacks = (packs: components['schemas']['Node'][]) =>
19+
packs.filter((pack) => !comfyManagerStore.isPackInstalled(pack.id))
20+
21+
// Filter only uninstalled packs from workflow packs
22+
const missingNodePacks = computed(() => {
23+
if (!workflowPacks.value.length) return []
24+
return filterMissingPacks(workflowPacks.value)
25+
})
26+
27+
// Automatically fetch workflow pack data when composable is used
28+
onMounted(async () => {
29+
if (!workflowPacks.value.length && !isLoading.value) {
30+
await startFetchWorkflowPacks()
31+
}
32+
})
33+
34+
return {
35+
missingNodePacks,
36+
isLoading,
37+
error
38+
}
39+
}

src/locales/en/main.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
"lastUpdated": "Last Updated",
162162
"noDescription": "No description available",
163163
"installSelected": "Install Selected",
164+
"installAllMissingNodes": "Install All Missing Nodes",
164165
"packsSelected": "Packs Selected",
165166
"status": {
166167
"active": "Active",

src/locales/es/main.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@
586586
},
587587
"inWorkflow": "En Flujo de Trabajo",
588588
"infoPanelEmpty": "Haz clic en un elemento para ver la información",
589+
"installAllMissingNodes": "Instalar todos los nodos faltantes",
589590
"installSelected": "Instalar Seleccionado",
590591
"installationQueue": "Cola de Instalación",
591592
"lastUpdated": "Última Actualización",

src/locales/fr/main.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@
586586
},
587587
"inWorkflow": "Dans le flux de travail",
588588
"infoPanelEmpty": "Cliquez sur un élément pour voir les informations",
589+
"installAllMissingNodes": "Installer tous les nœuds manquants",
589590
"installSelected": "Installer sélectionné",
590591
"installationQueue": "File d'attente d'installation",
591592
"lastUpdated": "Dernière mise à jour",

src/locales/ja/main.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@
586586
},
587587
"inWorkflow": "ワークフロー内",
588588
"infoPanelEmpty": "アイテムをクリックして情報を表示します",
589+
"installAllMissingNodes": "すべての不足しているノードをインストール",
589590
"installSelected": "選択したものをインストール",
590591
"installationQueue": "インストールキュー",
591592
"lastUpdated": "最終更新日",

0 commit comments

Comments
 (0)