[manager] Update UI components for new manager interface

Updated manager dialog components, pack cards, version selectors, and action buttons to work with the new manager API and state management structure.
This commit is contained in:
bymyself
2025-06-13 13:57:35 -07:00
committed by Jin Yi
parent 2157787367
commit 4a9abe0d00
11 changed files with 126 additions and 100 deletions

View File

@@ -18,7 +18,7 @@
'max-h-0': !isExpanded 'max-h-0': !isExpanded
}" }"
> >
<div v-for="(panel, index) in taskPanels" :key="index"> <div v-for="(log, index) in focusedLogs" :key="index">
<Panel <Panel
:expanded="collapsedPanels[index] || false" :expanded="collapsedPanels[index] || false"
toggleable toggleable
@@ -27,7 +27,7 @@
<template #header> <template #header>
<div class="flex items-center justify-between w-full py-2"> <div class="flex items-center justify-between w-full py-2">
<div class="flex flex-col text-sm font-medium leading-normal"> <div class="flex flex-col text-sm font-medium leading-normal">
<span>{{ panel.taskName }}</span> <span>{{ log.taskName }}</span>
<span class="text-muted"> <span class="text-muted">
{{ {{
isInProgress(index) isInProgress(index)
@@ -52,24 +52,24 @@
</template> </template>
<div <div
:ref=" :ref="
index === taskPanels.length - 1 index === focusedLogs.length - 1
? (el) => (lastPanelRef = el as HTMLElement) ? (el) => (lastPanelRef = el as HTMLElement)
: undefined : undefined
" "
class="overflow-y-auto h-64 rounded-lg bg-black" class="overflow-y-auto h-64 rounded-lg bg-black"
:class="{ :class="{
'h-64': index !== taskPanels.length - 1, 'h-64': index !== focusedLogs.length - 1,
'flex-grow': index === taskPanels.length - 1 'flex-grow': index === focusedLogs.length - 1
}" }"
@scroll="handleScroll" @scroll="handleScroll"
> >
<div class="h-full"> <div class="h-full">
<div <div
v-for="(log, logIndex) in panel.logs" v-for="(logLine, logIndex) in log.logs"
:key="logIndex" :key="logIndex"
class="text-neutral-400 dark-theme:text-muted" class="text-neutral-400 dark-theme:text-muted"
> >
<pre class="whitespace-pre-wrap break-words">{{ log }}</pre> <pre class="whitespace-pre-wrap break-words">{{ logLine }}</pre>
</div> </div>
</div> </div>
</div> </div>
@@ -90,17 +90,23 @@ import {
useManagerProgressDialogStore useManagerProgressDialogStore
} from '@/stores/comfyManagerStore' } from '@/stores/comfyManagerStore'
const { taskLogs } = useComfyManagerStore() const comfyManagerStore = useComfyManagerStore()
const progressDialogContent = useManagerProgressDialogStore() const progressDialogContent = useManagerProgressDialogStore()
const managerStore = useComfyManagerStore()
const isInProgress = (index: number) => const isInProgress = (index: number) =>
index === taskPanels.value.length - 1 && managerStore.uncompletedCount > 0 index === comfyManagerStore.managerQueue.historyCount - 1 &&
comfyManagerStore.isLoading
const taskPanels = computed(() => taskLogs)
const isExpanded = computed(() => progressDialogContent.isExpanded) const isExpanded = computed(() => progressDialogContent.isExpanded)
const isCollapsed = computed(() => !isExpanded.value) const isCollapsed = computed(() => !isExpanded.value)
const focusedLogs = computed(() => {
if (progressDialogContent.getActiveTabIndex() === 0) {
return comfyManagerStore.succeededTasksLogs
}
return comfyManagerStore.failedTasksLogs
})
const collapsedPanels = ref<Record<number, boolean>>({}) const collapsedPanels = ref<Record<number, boolean>>({})
const togglePanel = (index: number) => { const togglePanel = (index: number) => {
collapsedPanels.value[index] = !collapsedPanels.value[index] collapsedPanels.value[index] = !collapsedPanels.value[index]
@@ -115,7 +121,7 @@ const { y: scrollY } = useScroll(sectionsContainerRef, {
const lastPanelRef = ref<HTMLElement | null>(null) const lastPanelRef = ref<HTMLElement | null>(null)
const isUserScrolling = ref(false) const isUserScrolling = ref(false)
const lastPanelLogs = computed(() => taskPanels.value?.at(-1)?.logs) const lastPanelLogs = computed(() => focusedLogs.value?.at(-1)?.logs)
const isAtBottom = (el: HTMLElement | null) => { const isAtBottom = (el: HTMLElement | null) => {
if (!el) return false if (!el) return false

View File

@@ -1,12 +1,12 @@
import { VueWrapper, mount } from '@vue/test-utils' import { VueWrapper, mount } from '@vue/test-utils'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import Button from 'primevue/button'
import PrimeVue from 'primevue/config' import PrimeVue from 'primevue/config'
import { beforeEach, describe, expect, it, vi } from 'vitest' import { beforeEach, describe, expect, it, vi } from 'vitest'
import { nextTick } from 'vue' import { nextTick } from 'vue'
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
import enMessages from '@/locales/en/main.json' import enMessages from '@/locales/en/main.json'
import { SelectedVersion } from '@/types/comfyManagerTypes'
import PackVersionBadge from './PackVersionBadge.vue' import PackVersionBadge from './PackVersionBadge.vue'
import PackVersionSelectorPopover from './PackVersionSelectorPopover.vue' import PackVersionSelectorPopover from './PackVersionSelectorPopover.vue'
@@ -118,9 +118,9 @@ describe('PackVersionBadge', () => {
props: { nodePack: noVersionPack } props: { nodePack: noVersionPack }
}) })
const badge = wrapper.find('[role="button"]') const button = wrapper.findComponent(Button)
expect(badge.exists()).toBe(true) expect(button.exists()).toBe(true)
expect(badge.find('span').text()).toBe(SelectedVersion.NIGHTLY) expect(button.props('label')).toBe('nightly')
}) })
it('falls back to NIGHTLY when nodePack.id is missing', () => { it('falls back to NIGHTLY when nodePack.id is missing', () => {
@@ -132,9 +132,9 @@ describe('PackVersionBadge', () => {
props: { nodePack: invalidPack } props: { nodePack: invalidPack }
}) })
const badge = wrapper.find('[role="button"]') const button = wrapper.findComponent(Button)
expect(badge.exists()).toBe(true) expect(button.exists()).toBe(true)
expect(badge.find('span').text()).toBe(SelectedVersion.NIGHTLY) expect(button.props('label')).toBe('nightly')
}) })
it('toggles the popover when button is clicked', async () => { it('toggles the popover when button is clicked', async () => {

View File

@@ -42,8 +42,7 @@ import { computed, ref, watch } from 'vue'
import PackVersionSelectorPopover from '@/components/dialog/content/manager/PackVersionSelectorPopover.vue' import PackVersionSelectorPopover from '@/components/dialog/content/manager/PackVersionSelectorPopover.vue'
import { usePackUpdateStatus } from '@/composables/nodePack/usePackUpdateStatus' import { usePackUpdateStatus } from '@/composables/nodePack/usePackUpdateStatus'
import { useComfyManagerStore } from '@/stores/comfyManagerStore' import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import { SelectedVersion } from '@/types/comfyManagerTypes' import type { components } from '@/types/comfyRegistryTypes'
import { components } from '@/types/comfyRegistryTypes'
import { isSemVer } from '@/utils/formatUtil' import { isSemVer } from '@/utils/formatUtil'
const TRUNCATED_HASH_LENGTH = 7 const TRUNCATED_HASH_LENGTH = 7
@@ -64,11 +63,11 @@ const popoverRef = ref()
const managerStore = useComfyManagerStore() const managerStore = useComfyManagerStore()
const installedVersion = computed(() => { const installedVersion = computed(() => {
if (!nodePack.id) return SelectedVersion.NIGHTLY if (!nodePack.id) return 'nightly'
const version = const version =
managerStore.installedPacks[nodePack.id]?.ver ?? managerStore.installedPacks[nodePack.id]?.ver ??
nodePack.latest_version?.version ?? nodePack.latest_version?.version ??
SelectedVersion.NIGHTLY 'nightly'
// If Git hash, truncate to 7 characters // If Git hash, truncate to 7 characters
return isSemVer(version) ? version : version.slice(0, TRUNCATED_HASH_LENGTH) return isSemVer(version) ? version : version.slice(0, TRUNCATED_HASH_LENGTH)

View File

@@ -8,7 +8,8 @@ import { nextTick } from 'vue'
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
import enMessages from '@/locales/en/main.json' import enMessages from '@/locales/en/main.json'
import { SelectedVersion } from '@/types/comfyManagerTypes'
// SelectedVersion is now using direct strings instead of enum
import PackVersionSelectorPopover from './PackVersionSelectorPopover.vue' import PackVersionSelectorPopover from './PackVersionSelectorPopover.vue'
@@ -123,8 +124,8 @@ describe('PackVersionSelectorPopover', () => {
expect(options.length).toBe(defaultMockVersions.length + 2) // 2 special options + version options expect(options.length).toBe(defaultMockVersions.length + 2) // 2 special options + version options
// Check that special options exist // Check that special options exist
expect(options.some((o) => o.value === SelectedVersion.NIGHTLY)).toBe(true) expect(options.some((o) => o.value === 'nightly')).toBe(true)
expect(options.some((o) => o.value === SelectedVersion.LATEST)).toBe(true) expect(options.some((o) => o.value === 'latest')).toBe(true)
// Check that version options exist // Check that version options exist
expect(options.some((o) => o.value === '1.0.0')).toBe(true) expect(options.some((o) => o.value === '1.0.0')).toBe(true)
@@ -304,7 +305,7 @@ describe('PackVersionSelectorPopover', () => {
await waitForPromises() await waitForPromises()
const listbox = wrapper.findComponent(Listbox) const listbox = wrapper.findComponent(Listbox)
expect(listbox.exists()).toBe(true) expect(listbox.exists()).toBe(true)
expect(listbox.props('modelValue')).toBe(SelectedVersion.NIGHTLY) expect(listbox.props('modelValue')).toBe('nightly')
}) })
it('defaults to nightly when publisher name is "Unclaimed"', async () => { it('defaults to nightly when publisher name is "Unclaimed"', async () => {
@@ -325,7 +326,7 @@ describe('PackVersionSelectorPopover', () => {
await waitForPromises() await waitForPromises()
const listbox = wrapper.findComponent(Listbox) const listbox = wrapper.findComponent(Listbox)
expect(listbox.exists()).toBe(true) expect(listbox.exists()).toBe(true)
expect(listbox.props('modelValue')).toBe(SelectedVersion.NIGHTLY) expect(listbox.props('modelValue')).toBe('nightly')
}) })
}) })
}) })

View File

@@ -69,12 +69,8 @@ import ContentDivider from '@/components/common/ContentDivider.vue'
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue' import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
import { useComfyRegistryService } from '@/services/comfyRegistryService' import { useComfyRegistryService } from '@/services/comfyRegistryService'
import { useComfyManagerStore } from '@/stores/comfyManagerStore' import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import { import type { components } from '@/types/comfyRegistryTypes'
ManagerChannel, import { components as ManagerComponents } from '@/types/generatedManagerTypes'
ManagerDatabaseSource,
SelectedVersion
} from '@/types/comfyManagerTypes'
import { components } from '@/types/comfyRegistryTypes'
import { isSemVer } from '@/utils/formatUtil' import { isSemVer } from '@/utils/formatUtil'
const { nodePack } = defineProps<{ const { nodePack } = defineProps<{
@@ -92,19 +88,20 @@ const managerStore = useComfyManagerStore()
const isQueueing = ref(false) const isQueueing = ref(false)
const selectedVersion = ref<string>(SelectedVersion.LATEST) const selectedVersion = ref<string>('latest')
onMounted(() => { onMounted(() => {
const initialVersion = getInitialSelectedVersion() ?? SelectedVersion.LATEST const initialVersion = getInitialSelectedVersion() ?? 'latest'
selectedVersion.value = selectedVersion.value =
// Use NIGHTLY when version is a Git hash // Use NIGHTLY when version is a Git hash
isSemVer(initialVersion) ? initialVersion : SelectedVersion.NIGHTLY isSemVer(initialVersion) ? initialVersion : 'nightly'
}) })
const getInitialSelectedVersion = () => { const getInitialSelectedVersion = () => {
if (!nodePack.id) return if (!nodePack.id) return
// If unclaimed, set selected version to nightly // If unclaimed, set selected version to nightly
if (nodePack.publisher?.name === 'Unclaimed') return SelectedVersion.NIGHTLY if (nodePack.publisher?.name === 'Unclaimed')
return 'nightly' as ManagerComponents['schemas']['SelectedVersion']
// If node pack is installed, set selected version to the installed version // If node pack is installed, set selected version to the installed version
if (managerStore.isPackInstalled(nodePack.id)) if (managerStore.isPackInstalled(nodePack.id))
@@ -143,7 +140,7 @@ const onNodePackChange = async () => {
// Add Latest option // Add Latest option
const defaultVersions = [ const defaultVersions = [
{ {
value: SelectedVersion.LATEST, value: 'latest' as ManagerComponents['schemas']['SelectedVersion'],
label: t('manager.latestVersion') label: t('manager.latestVersion')
} }
] ]
@@ -151,7 +148,7 @@ const onNodePackChange = async () => {
// Add Nightly option if there is a non-empty `repository` field // Add Nightly option if there is a non-empty `repository` field
if (nodePack.repository?.length) { if (nodePack.repository?.length) {
defaultVersions.push({ defaultVersions.push({
value: SelectedVersion.NIGHTLY, value: 'nightly' as ManagerComponents['schemas']['SelectedVersion'],
label: t('manager.nightlyVersion') label: t('manager.nightlyVersion')
}) })
} }
@@ -172,12 +169,16 @@ whenever(
const handleSubmit = async () => { const handleSubmit = async () => {
isQueueing.value = true isQueueing.value = true
if (!nodePack.id) {
throw new Error('Node ID is required for installation')
}
await managerStore.installPack.call({ await managerStore.installPack.call({
id: nodePack.id, id: nodePack.id,
repository: nodePack.repository ?? '',
channel: ManagerChannel.DEFAULT,
mode: ManagerDatabaseSource.CACHE,
version: selectedVersion.value, version: selectedVersion.value,
repository: nodePack.repository ?? '',
channel: 'default' as ManagerComponents['schemas']['ManagerChannel'],
mode: 'cache' as ManagerComponents['schemas']['ManagerDatabaseSource'],
selected_version: selectedVersion.value selected_version: selectedVersion.value
}) })

View File

@@ -15,12 +15,8 @@ import ToggleSwitch from 'primevue/toggleswitch'
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useComfyManagerStore } from '@/stores/comfyManagerStore' import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import {
InstallPackParams,
ManagerChannel,
SelectedVersion
} from '@/types/comfyManagerTypes'
import type { components } from '@/types/comfyRegistryTypes' import type { components } from '@/types/comfyRegistryTypes'
import { components as ManagerComponents } from '@/types/generatedManagerTypes'
const TOGGLE_DEBOUNCE_MS = 256 const TOGGLE_DEBOUNCE_MS = 256
@@ -28,37 +24,42 @@ const { nodePack } = defineProps<{
nodePack: components['schemas']['Node'] nodePack: components['schemas']['Node']
}>() }>()
const { isPackEnabled, enablePack, disablePack, installedPacks } = const { isPackEnabled, enablePack, disablePack } = useComfyManagerStore()
useComfyManagerStore()
const isLoading = ref(false) const isLoading = ref(false)
const isEnabled = computed(() => isPackEnabled(nodePack.id)) const isEnabled = computed(() => isPackEnabled(nodePack.id))
const version = computed(() => {
const id = nodePack.id
if (!id) return SelectedVersion.NIGHTLY
return (
installedPacks[id]?.ver ??
nodePack.latest_version?.version ??
SelectedVersion.NIGHTLY
)
})
const handleEnable = () => const handleEnable = () => {
enablePack.call({ if (!nodePack.id) {
throw new Error('Node ID is required for enabling')
}
return enablePack.call({
id: nodePack.id, id: nodePack.id,
version: version.value, version:
selected_version: version.value, nodePack.latest_version?.version ??
('latest' as ManagerComponents['schemas']['SelectedVersion']),
selected_version:
nodePack.latest_version?.version ??
('latest' as ManagerComponents['schemas']['SelectedVersion']),
repository: nodePack.repository ?? '', repository: nodePack.repository ?? '',
channel: ManagerChannel.DEFAULT, channel: 'default' as ManagerComponents['schemas']['ManagerChannel'],
mode: 'default' as InstallPackParams['mode'] mode: 'cache' as ManagerComponents['schemas']['ManagerDatabaseSource'],
skip_post_install: false
}) })
}
const handleDisable = () => const handleDisable = () => {
disablePack({ if (!nodePack.id) {
throw new Error('Node ID is required for disabling')
}
return disablePack({
id: nodePack.id, id: nodePack.id,
version: version.value version:
nodePack.latest_version?.version ??
('latest' as ManagerComponents['schemas']['SelectedVersion'])
}) })
}
const handleToggle = async (enable: boolean) => { const handleToggle = async (enable: boolean) => {
if (isLoading.value) return if (isLoading.value) return
@@ -67,10 +68,16 @@ const handleToggle = async (enable: boolean) => {
if (enable) { if (enable) {
await handleEnable() await handleEnable()
} else { } else {
handleDisable() await handleDisable()
} }
isLoading.value = false isLoading.value = false
} }
const onToggle = debounce(handleToggle, TOGGLE_DEBOUNCE_MS, { trailing: true }) const onToggle = debounce(
(enable: boolean) => {
void handleToggle(enable)
},
TOGGLE_DEBOUNCE_MS,
{ trailing: true }
)
</script> </script>

View File

@@ -19,13 +19,9 @@ import { inject, ref } from 'vue'
import PackActionButton from '@/components/dialog/content/manager/button/PackActionButton.vue' import PackActionButton from '@/components/dialog/content/manager/button/PackActionButton.vue'
import { useComfyManagerStore } from '@/stores/comfyManagerStore' import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import { import { IsInstallingKey } from '@/types/comfyManagerTypes'
IsInstallingKey,
ManagerChannel,
ManagerDatabaseSource,
SelectedVersion
} from '@/types/comfyManagerTypes'
import type { components } from '@/types/comfyRegistryTypes' import type { components } from '@/types/comfyRegistryTypes'
import { components as ManagerComponents } from '@/types/generatedManagerTypes'
type NodePack = components['schemas']['Node'] type NodePack = components['schemas']['Node']
@@ -43,19 +39,26 @@ const onClick = (): void => {
const managerStore = useComfyManagerStore() const managerStore = useComfyManagerStore()
const createPayload = (installItem: NodePack) => { const createPayload = (
installItem: NodePack
): ManagerComponents['schemas']['InstallPackParams'] => {
if (!installItem.id) {
throw new Error('Node ID is required for installation')
}
const isUnclaimedPack = installItem.publisher?.name === 'Unclaimed' const isUnclaimedPack = installItem.publisher?.name === 'Unclaimed'
const versionToInstall = isUnclaimedPack const versionToInstall = isUnclaimedPack
? SelectedVersion.NIGHTLY ? ('nightly' as ManagerComponents['schemas']['SelectedVersion'])
: installItem.latest_version?.version ?? SelectedVersion.LATEST : installItem.latest_version?.version ??
('latest' as ManagerComponents['schemas']['SelectedVersion'])
return { return {
id: installItem.id, id: installItem.id,
version: versionToInstall,
repository: installItem.repository ?? '', repository: installItem.repository ?? '',
channel: ManagerChannel.DEV, channel: 'dev' as ManagerComponents['schemas']['ManagerChannel'],
mode: ManagerDatabaseSource.CACHE, mode: 'cache' as ManagerComponents['schemas']['ManagerDatabaseSource'],
selected_version: versionToInstall, selected_version: versionToInstall
version: versionToInstall
} }
} }
@@ -65,7 +68,7 @@ const installPack = (item: NodePack) =>
const installAllPacks = async () => { const installAllPacks = async () => {
if (!nodePacks?.length) return if (!nodePacks?.length) return
isInstalling.value = true // isInstalling.value = true
const uninstalledPacks = nodePacks.filter( const uninstalledPacks = nodePacks.filter(
(pack) => !managerStore.isPackInstalled(pack.id) (pack) => !managerStore.isPackInstalled(pack.id)

View File

@@ -15,7 +15,6 @@
<script setup lang="ts"> <script setup lang="ts">
import PackActionButton from '@/components/dialog/content/manager/button/PackActionButton.vue' import PackActionButton from '@/components/dialog/content/manager/button/PackActionButton.vue'
import { useComfyManagerStore } from '@/stores/comfyManagerStore' import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import type { ManagerPackInfo } from '@/types/comfyManagerTypes'
import type { components } from '@/types/comfyRegistryTypes' import type { components } from '@/types/comfyRegistryTypes'
type NodePack = components['schemas']['Node'] type NodePack = components['schemas']['Node']
@@ -26,16 +25,16 @@ const { nodePacks } = defineProps<{
const managerStore = useComfyManagerStore() const managerStore = useComfyManagerStore()
const createPayload = (uninstallItem: NodePack): ManagerPackInfo => { const uninstallPack = (item: NodePack) => {
return { if (!item.id) {
id: uninstallItem.id, throw new Error('Node ID is required for uninstallation')
version: uninstallItem.latest_version?.version
} }
return managerStore.uninstallPack({
id: item.id,
version: item.latest_version?.version ?? ''
})
} }
const uninstallPack = (item: NodePack) =>
managerStore.uninstallPack(createPayload(item))
const uninstallItems = async () => { const uninstallItems = async () => {
if (!nodePacks?.length) return if (!nodePacks?.length) return
await Promise.all(nodePacks.map(uninstallPack)) await Promise.all(nodePacks.map(uninstallPack))

View File

@@ -119,12 +119,20 @@ provide(IsInstallingKey, isInstalling)
const { isPackInstalled, isPackEnabled } = useComfyManagerStore() const { isPackInstalled, isPackEnabled } = useComfyManagerStore()
const isInstalled = computed(() => isPackInstalled(nodePack?.id)) const isDisabled = ref(false)
const isDisabled = computed( const managerStore = useComfyManagerStore()
() => isInstalled.value && !isPackEnabled(nodePack?.id)
)
whenever(isInstalled, () => (isInstalling.value = false)) // Watch the installedPacks object directly (which gets updated from WebSocket)
whenever(
() => managerStore.installedPacksIds,
() => {
const isInstalled = isPackInstalled(nodePack?.id)
isDisabled.value = isInstalled && !isPackEnabled(nodePack?.id)
// Update isInstalling state after installation is complete
if (isInstalling.value && isInstalled) isInstalling.value = false
}
)
const nodesCount = computed(() => const nodesCount = computed(() =>
isMergedNodePack(nodePack) ? nodePack.comfy_nodes?.length : undefined isMergedNodePack(nodePack) ? nodePack.comfy_nodes?.length : undefined

View File

@@ -83,7 +83,7 @@ const dialogStore = useDialogStore()
const progressDialogContent = useManagerProgressDialogStore() const progressDialogContent = useManagerProgressDialogStore()
const comfyManagerStore = useComfyManagerStore() const comfyManagerStore = useComfyManagerStore()
const isInProgress = computed(() => comfyManagerStore.uncompletedCount > 0) const isInProgress = computed(() => comfyManagerStore.isProcessingTasks)
const closeDialog = () => { const closeDialog = () => {
dialogStore.closeDialog({ key: 'global-manager-progress-dialog' }) dialogStore.closeDialog({ key: 'global-manager-progress-dialog' })

View File

@@ -18,13 +18,15 @@
<script setup lang="ts"> <script setup lang="ts">
import TabMenu from 'primevue/tabmenu' import TabMenu from 'primevue/tabmenu'
import { ref } from 'vue' import { computed } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useManagerProgressDialogStore } from '@/stores/comfyManagerStore' import { useManagerProgressDialogStore } from '@/stores/comfyManagerStore'
const progressDialogContent = useManagerProgressDialogStore() const progressDialogContent = useManagerProgressDialogStore()
const activeTabIndex = ref(0) const activeTabIndex = computed(() => {
return progressDialogContent.getActiveTabIndex()
})
const { t } = useI18n() const { t } = useI18n()
const tabs = [ const tabs = [
{ label: t('manager.installationQueue') }, { label: t('manager.installationQueue') },