[backport cloud/1.36] feat: add model download progress dialog (#7917)

Backport of #7897 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7917-backport-cloud-1-36-feat-add-model-download-progress-dialog-2e36d73d365081b18bddeb4835f4d706)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Comfy Org PR Bot
2026-01-09 10:29:03 +09:00
committed by GitHub
parent aecb841cc0
commit e912b42fff
7 changed files with 413 additions and 65 deletions

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
type Severity = 'default' | 'secondary' | 'warn' | 'danger' | 'contrast'
const { label, severity = 'default' } = defineProps<{
label: string
severity?: Severity
}>()
function badgeClasses(sev: Severity): string {
const baseClasses =
'inline-flex h-3.5 items-center justify-center rounded-full px-1 text-xxxs font-semibold uppercase'
switch (sev) {
case 'danger':
return `${baseClasses} bg-destructive-background text-white`
case 'contrast':
return `${baseClasses} bg-base-foreground text-base-background`
case 'warn':
return `${baseClasses} bg-warning-background text-base-background`
case 'secondary':
return `${baseClasses} bg-secondary-background text-base-foreground`
default:
return `${baseClasses} bg-primary-background text-base-foreground`
}
}
</script>
<template>
<span :class="badgeClasses(severity)">{{ label }}</span>
</template>

View File

@@ -0,0 +1,66 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import StatusBadge from '@/components/common/StatusBadge.vue'
import type { AssetDownload } from '@/stores/assetDownloadStore'
import { cn } from '@/utils/tailwindUtil'
const { job } = defineProps<{
job: AssetDownload
}>()
const { t } = useI18n()
const progressPercent = computed(() => Math.round(job.progress * 100))
const isCompleted = computed(() => job.status === 'completed')
const isFailed = computed(() => job.status === 'failed')
const isRunning = computed(() => job.status === 'running')
const isPending = computed(() => job.status === 'created')
</script>
<template>
<div
:class="
cn(
'flex items-center justify-between rounded-lg bg-modal-card-background px-4 py-3',
isCompleted && 'opacity-50'
)
"
>
<div class="flex flex-col">
<span class="text-sm text-base-foreground">{{ job.assetName }}</span>
<span v-if="isRunning" class="text-xs text-muted-foreground">
{{ progressPercent }}%
</span>
</div>
<div class="flex items-center gap-2">
<template v-if="isFailed">
<i
class="icon-[lucide--circle-alert] size-4 text-destructive-background"
/>
<StatusBadge :label="t('progressToast.failed')" severity="danger" />
</template>
<template v-else-if="isCompleted">
<StatusBadge :label="t('progressToast.finished')" severity="contrast" />
</template>
<template v-else-if="isRunning">
<i
class="icon-[lucide--loader-circle] size-4 animate-spin text-primary-background"
/>
<span class="text-xs text-primary-background">
{{ progressPercent }}%
</span>
</template>
<template v-else-if="isPending">
<span class="text-xs text-muted-foreground">
{{ t('progressToast.pending') }}
</span>
</template>
</div>
</div>
</template>